Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 19 from a total of 19 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Create Obol Vali... | 1841131 | 26 days ago | IN | 0 ETH | 0.00310274 | ||||
| Create Obol Vali... | 1841131 | 26 days ago | IN | 0 ETH | 0.00310274 | ||||
| Create Obol Vali... | 1841129 | 26 days ago | IN | 0 ETH | 0.00321653 | ||||
| Create Obol Vali... | 1841129 | 26 days ago | IN | 0 ETH | 0.00321656 | ||||
| Create Obol Vali... | 1841129 | 26 days ago | IN | 0 ETH | 0.00321656 | ||||
| Create Obol Vali... | 1841129 | 26 days ago | IN | 0 ETH | 0.00321656 | ||||
| Create Obol Vali... | 1841128 | 26 days ago | IN | 0 ETH | 0.00319606 | ||||
| Create Obol Vali... | 1841126 | 26 days ago | IN | 0 ETH | 0.00306339 | ||||
| Create Obol Vali... | 1841126 | 26 days ago | IN | 0 ETH | 0.00306339 | ||||
| Create Obol Vali... | 1807091 | 31 days ago | IN | 0 ETH | 0.00315373 | ||||
| Create Obol Vali... | 1807088 | 31 days ago | IN | 0 ETH | 0.00329257 | ||||
| Create Obol Vali... | 1787142 | 34 days ago | IN | 0 ETH | 0.00323119 | ||||
| Create Obol Vali... | 1782249 | 35 days ago | IN | 0 ETH | 0.00340124 | ||||
| Create Obol Vali... | 1782247 | 35 days ago | IN | 0 ETH | 0.0032668 | ||||
| Create Obol Vali... | 1760825 | 38 days ago | IN | 0 ETH | 0.00328034 | ||||
| Create Obol Vali... | 1760821 | 38 days ago | IN | 0 ETH | 0.00302292 | ||||
| Create Obol Vali... | 1760153 | 38 days ago | IN | 0 ETH | 0.00322391 | ||||
| Create Obol Vali... | 1760151 | 38 days ago | IN | 0 ETH | 0.00310984 | ||||
| Create Obol Vali... | 1735402 | 41 days ago | IN | 0 ETH | 0.00003174 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
Amount
|
||
|---|---|---|---|---|---|---|---|
| 0x61010060 | 1990698 | 4 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1990698 | 4 days ago | 0 ETH | ||||
| 0x61010060 | 1946981 | 10 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1946981 | 10 days ago | 0 ETH | ||||
| 0x61010060 | 1841134 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841134 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841134 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841134 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841133 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841133 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841133 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841133 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841131 | 26 days ago | Contract Creation | 0 ETH | |||
| 0x61010060 | 1841131 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841131 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841131 | 26 days ago | Contract Creation | 0 ETH | |||
| 0x61010060 | 1841131 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841131 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841130 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841130 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841130 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841130 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841130 | 26 days ago | Contract Creation | 0 ETH | |||
| Create Obol Vali... | 1841130 | 26 days ago | 0 ETH | ||||
| 0x61010060 | 1841130 | 26 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Contract Name:
ObolValidatorManagerFactory
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity)
/**
*Submitted for verification at Etherscan.io on 2025-12-01
*/
// SPDX-License-Identifier: Proprietary
pragma solidity =0.8.19 >=0.8.0 ^0.8.19 ^0.8.4;
// 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);
}
// src/interfaces/IObolValidatorManager.sol
/// @title IObolValidatorManager
/// @author Obol
/// @dev The interface for ObolValidatorManager contract.
interface IObolValidatorManager {
/// @notice Struct to represent a consolidation request.
/// @param srcPubKeys The public keys of the validators to consolidate from.
/// @param targetPubKey The public key of the validator to consolidate to.
struct ConsolidationRequest {
bytes[] srcPubKeys;
bytes targetPubKey;
}
/// -----------------------------------------------------------------------
/// Events
/// -----------------------------------------------------------------------
/// @notice Emitted when the beneficiary address is updated.
/// @param newBeneficiary The new beneficiary address.
event BeneficiaryUpdated(address indexed newBeneficiary);
/// @notice Emitted when the reward recipient is updated.
/// @param newRewardRecipient New reward recipient address
event RewardRecipientUpdated(address indexed newRewardRecipient);
/// @notice Emitted when the amount of principal stake is updated.
/// @param newPrincipalStakeAmount New amount of principal stake (wei)
/// @param oldPrincipalStakeAmount Old amount of principal stake (wei)
event PrincipalStakeAmountUpdated(uint256 newPrincipalStakeAmount, uint256 oldPrincipalStakeAmount);
/// @notice Emitted when funds are swept to the beneficiary.
/// @param beneficiary The address to which funds were swept.
/// @param amount The amount of funds swept.
event Swept(address indexed beneficiary, uint256 indexed amount);
/// @notice 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);
/// @notice 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);
/// @notice Emitted after funds withdrawn using pull flow
/// @param account Account withdrawing funds for
/// @param amount Amount withdrawn
event PullBalanceWithdrawn(address indexed account, uint256 amount);
/// @notice Emitted when a consolidation request is submitted.
/// @param srcPubKey The public key of the source validator.
/// @param targetPubKey The public key of the target validator.
/// @param fee The fee paid for the consolidation.
event ConsolidationRequested(bytes srcPubKey, bytes targetPubKey, uint256 indexed fee);
/// @notice Emitted when a withdrawal request is submitted for a validator.
/// @param pubKey The public key of the validator.
/// @param amount The amount to withdraw from the validator.
/// @param fee The fee paid for the withdrawal.
event WithdrawalRequested(bytes pubKey, uint64 indexed amount, uint256 indexed fee);
/// @notice Emitted when the excess fee sent as part of a consolidation or withdrawal (partial or full)
/// request could not be refunded to the excessFeeRecipient.
/// @param excessFeeRecipient The address to which the excess fee should have been sent.
/// @param excessFee The amount of excess fee sent.
event UnsentExcessFee(address indexed excessFeeRecipient, uint256 indexed excessFee);
/// @notice Emitted when the ownership of the TVS is transferred to a new owner.
/// @param newBeneficiary The address of the new beneficiary.
/// @param newOwner The address of the new owner.
event Transferred(address indexed newBeneficiary, address indexed newOwner);
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
/// @notice Invalid request params, e.g. empty input
error InvalidRequest_Params();
/// @notice Failed to call system contract get_fee()
error InvalidRequest_SystemGetFee();
/// @notice Insufficient fee provided in the call's value to conclude the request
error InvalidRequest_NotEnoughFee();
/// @notice Failed to call system contract add_consolidation_request()
error InvalidConsolidation_Failed();
/// @notice Failed to call system contract add_withdrawal_request()
error InvalidWithdrawal_Failed();
/// @notice Invalid distribution
error InvalidDistribution_TooLarge();
/// -----------------------------------------------------------------------
/// ObolValidatorManager functions
/// -----------------------------------------------------------------------
/// @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 Change the address that receives the principal
/// @param newBeneficiary New beneficiary recipient address to set
function setBeneficiary(address newBeneficiary) external;
/// @notice Overrides the current amount of principal stake
/// @param newAmount New amount of principal stake (wei)
/// @dev The amount of principal stake is usually increased via the deposit() call,
/// but in certain cases, it may need to be changed explicitly.
function setAmountOfPrincipalStake(uint256 newAmount) external;
/// @notice Set the reward recipient address
/// @param newRewardRecipient New address to receive reward funds
function setRewardRecipient(address newRewardRecipient) external;
/// @notice Sweeps a specific amount, or all ETH pending withdrawal to the beneficiary or a specified address.
/// @dev Emits {Swept} event.
/// @param beneficiary Address to which funds will be swept, if zero address, sweeps to the beneficiary address set
/// on the contract
/// @param amount Amount of funds to sweep, if zero, sweeps all funds pending withdrawal for the beneficiary
function sweep(address beneficiary, uint256 amount) external;
/// @notice This function always reverts with "Not implemented" message.
function sweepToBeneficiaryContract(address, uint256) external;
/// @notice Distributes target token inside the contract to recipients
/// @dev Pushes funds to recipients
function distributeFunds() external;
/// @notice 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;
/// @notice Consolidates validators using the EIP7251 system contract
/// @dev The excess fee is the difference between the maximum fee and the actual fee paid.
/// @dev Emits a {UnsentExcessFee} event if the excess fee is not sent.
/// @param requests An array of consolidation requests.
/// @param maxFeePerConsolidation The maximum fee allowed per consolidation request.
/// @param excessFeeRecipient The address to which excess fees will be sent.
function consolidate(
ConsolidationRequest[] calldata requests,
uint256 maxFeePerConsolidation,
address excessFeeRecipient
) external payable;
/// @notice Withdraws from validators using 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.
/// Zero amount will trigger a full withdrawal of the validator.
/// @param maxFeePerWithdrawal The maximum fee allowed per withdrawal.
/// @param excessFeeRecipient The address to which excess fees will be sent.
function withdraw(
bytes[] calldata pubKeys,
uint64[] calldata amounts,
uint256 maxFeePerWithdrawal,
address excessFeeRecipient
) external payable;
/// @notice Recovers 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;
/// @notice Withdraws pull balance for an account
/// @param account Address to withdraw pull balance for
function withdrawPullBalance(address account) external;
/// @notice 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);
/// @notice Transfers the ownership of the OVM.
/// @dev This function sets a new beneficiary, transfers ownership to a new owner.
/// @dev Only the owner can call this function.
/// @dev Emits a {Transferred} event.
/// @param newBeneficiary The new beneficiary address.
/// @param newOwner The new owner address.
function transfer(address newBeneficiary, address newOwner) external;
/// -----------------------------------------------------------------------
/// ObolValidatorManager variable getters
/// -----------------------------------------------------------------------
/// @notice Returns the consolidation system contract address
/// @return Address of the EIP7251 consolidation system contract
function consolidationSystemContract() external view returns (address);
/// @notice Returns the withdrawal system contract address
/// @return Address of the EIP7002 withdrawal system contract
function withdrawalSystemContract() external view returns (address);
/// @notice Returns the deposit system contract address
/// @return Address of the Ethereum deposit contract
function depositSystemContract() external view returns (address);
/// @notice Returns the current principal recipient address
/// @return Address that receives principal stake distributions
function getBeneficiary() external view returns (address);
/// @notice Returns the reward recipient address
/// @return Address that receives reward distributions
function rewardRecipient() external view returns (address);
/// @notice Returns the principal classification threshold
/// @return Threshold in gwei for classifying withdrawals as principal vs reward
function principalThreshold() external view returns (uint64);
/// @notice Returns the total amount of principal stake deposited
/// @return Total principal stake in wei deposited via deposit() function
function amountOfPrincipalStake() external view returns (uint256);
/// @notice Returns the amount of funds pending withdrawal in pull flow
/// @return Amount in wei set aside for pull withdrawals
function fundsPendingWithdrawal() external view returns (uint128);
/// -----------------------------------------------------------------------
/// OwnableRoles functions
/// -----------------------------------------------------------------------
/// @dev Allows the owner to grant `user` `roles`.
/// If the `user` already has a role, then it will be a no-op for the role.
function grantRoles(address user, uint256 roles) external payable;
/// @dev Allows the owner to remove `user` `roles`.
/// If the `user` does not have a role, then it will be a no-op for the role.
function revokeRoles(address user, uint256 roles) external payable;
/// @dev Allow the caller to remove their own roles.
/// If the caller does not have a role, then it will be a no-op for the role.
function renounceRoles(uint256 roles) external payable;
/// @dev Returns the roles of `user`.
function rolesOf(address user) external view returns (uint256 roles);
/// @dev Returns whether `user` has any of `roles`.
function hasAnyRole(address user, uint256 roles) external view returns (bool);
/// @dev Returns whether `user` has all of `roles`.
function hasAllRoles(address user, uint256 roles) external view returns (bool);
/// -----------------------------------------------------------------------
/// Ownable functions
/// -----------------------------------------------------------------------
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) external payable;
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() external payable;
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() external payable;
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() external payable;
/// @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) external payable;
/// @dev Returns the owner of the contract.
function owner() external view returns (address result);
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner) external view returns (uint256 result);
}
// 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/solmate/src/utils/ReentrancyGuard.sol
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
// 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/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 its recipients.
contract ObolValidatorManager is IObolValidatorManager, OwnableRoles, ReentrancyGuard {
/// -----------------------------------------------------------------------
/// libraries
/// -----------------------------------------------------------------------
using SafeTransferLib for address;
/// -----------------------------------------------------------------------
/// storage
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// storage - constants
/// -----------------------------------------------------------------------
uint256 public constant WITHDRAWAL_ROLE = 0x01;
uint256 public constant CONSOLIDATION_ROLE = 0x02;
uint256 public constant SET_BENEFICIARY_ROLE = 0x04;
uint256 public constant RECOVER_FUNDS_ROLE = 0x08;
uint256 public constant SET_REWARD_ROLE = 0x10;
uint256 public constant DEPOSIT_ROLE = 0x20;
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;
uint64 public immutable principalThreshold;
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// Address to receive principal funds
address public principalRecipient;
/// Address to receive reward funds
address public rewardRecipient;
/// Amount of principal stake (wei)
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 _beneficiary,
address _rewardRecipient,
uint64 _principalThreshold
) {
if (_consolidationSystemContract == address(0)) revert InvalidRequest_Params();
if (_withdrawalSystemContract == address(0)) revert InvalidRequest_Params();
if (_depositSystemContract == address(0)) revert InvalidRequest_Params();
if (_owner == address(0)) revert InvalidRequest_Params();
if (_beneficiary == address(0)) revert InvalidRequest_Params();
if (_rewardRecipient == address(0)) revert InvalidRequest_Params();
consolidationSystemContract = _consolidationSystemContract;
withdrawalSystemContract = _withdrawalSystemContract;
depositSystemContract = _depositSystemContract;
principalRecipient = _beneficiary;
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 {}
/// @inheritdoc IObolValidatorManager
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable onlyOwnerOrRoles(DEPOSIT_ROLE) {
uint256 oldAmountOfPrincipalStake = amountOfPrincipalStake;
amountOfPrincipalStake += msg.value;
IDepositContract(depositSystemContract).deposit{value: msg.value}(
pubkey,
withdrawal_credentials,
signature,
deposit_data_root
);
emit PrincipalStakeAmountUpdated(amountOfPrincipalStake, oldAmountOfPrincipalStake);
}
/// @inheritdoc IObolValidatorManager
function setBeneficiary(address newBeneficiary) external onlyOwnerOrRoles(SET_BENEFICIARY_ROLE) {
if (newBeneficiary == address(0)) revert InvalidRequest_Params();
principalRecipient = newBeneficiary;
emit BeneficiaryUpdated(newBeneficiary);
}
/// @inheritdoc IObolValidatorManager
function setAmountOfPrincipalStake(uint256 newAmount) external onlyOwnerOrRoles(SET_BENEFICIARY_ROLE) {
if (newAmount == amountOfPrincipalStake) return;
uint256 oldAmount = amountOfPrincipalStake;
amountOfPrincipalStake = newAmount;
emit PrincipalStakeAmountUpdated(newAmount, oldAmount);
}
/// @inheritdoc IObolValidatorManager
function setRewardRecipient(address newRewardRecipient) external onlyOwnerOrRoles(SET_REWARD_ROLE) {
if (newRewardRecipient == address(0)) revert InvalidRequest_Params();
rewardRecipient = newRewardRecipient;
emit RewardRecipientUpdated(newRewardRecipient);
}
/// @inheritdoc IObolValidatorManager
function sweep(address beneficiary, uint256 amount) external nonReentrant {
address recipient = principalRecipient;
if (beneficiary != address(0)) {
_checkOwner();
recipient = beneficiary;
}
// If amount is zero, sweep all funds in pullBalances for principalRecipient
uint256 sweepAmount = amount == 0 ? pullBalances[principalRecipient] : amount;
if (sweepAmount > pullBalances[principalRecipient]) revert InvalidRequest_Params();
unchecked {
// shouldn't underflow; fundsPendingWithdrawal = sum(pullBalances)
fundsPendingWithdrawal -= uint128(sweepAmount);
}
pullBalances[principalRecipient] -= sweepAmount;
emit Swept(recipient, sweepAmount);
recipient.safeTransferETH(sweepAmount);
}
/// @inheritdoc IObolValidatorManager
function sweepToBeneficiaryContract(address, uint256) external nonReentrant {
revert("Not implemented");
}
/// @inheritdoc IObolValidatorManager
function distributeFunds() external nonReentrant {
_distributeFunds(PUSH);
}
/// @inheritdoc IObolValidatorManager
function distributeFundsPull() external nonReentrant {
_distributeFunds(PULL);
}
/// @inheritdoc IObolValidatorManager
function consolidate(
ConsolidationRequest[] calldata requests,
uint256 maxFeePerConsolidation,
address excessFeeRecipient
) external payable onlyOwnerOrRoles(CONSOLIDATION_ROLE) nonReentrant {
// Check if fee exceeds maximum allowed, otherwise get fee
uint256 fee = _validateAndReturnFee(consolidationSystemContract, maxFeePerConsolidation);
// Calculate total number of consolidation operations
uint256 totalNumOfConsolidationOperations = 0;
for (uint256 i = 0; i < requests.length; i++) {
if (requests[i].srcPubKeys.length == 0 || requests[i].srcPubKeys.length > 63) revert InvalidRequest_Params();
totalNumOfConsolidationOperations += requests[i].srcPubKeys.length;
}
// Check if the msg.value is enough to cover the fees
uint256 totalFeeRequired = fee * totalNumOfConsolidationOperations;
_validateSufficientValueForFee(msg.value, totalFeeRequired);
// Perform the consolidation requests
for (uint256 i = 0; i < requests.length; i++) {
_validatePubkeyLength(requests[i].targetPubKey);
for (uint256 j = 0; j < requests[i].srcPubKeys.length; j++) {
_validatePubkeyLength(requests[i].srcPubKeys[j]);
// Add the consolidation request
bytes memory callData = bytes.concat(requests[i].srcPubKeys[j], requests[i].targetPubKey);
(bool success, ) = consolidationSystemContract.call{value: fee}(callData);
if (!success) revert InvalidConsolidation_Failed();
// Emit consolidation event for each operation
emit ConsolidationRequested(requests[i].srcPubKeys[j], requests[i].targetPubKey, fee);
}
}
// Refund any excess value back to the excessFeeRecipient
_refundExcessFee(msg.value, totalFeeRequired, excessFeeRecipient);
}
/// @inheritdoc IObolValidatorManager
function withdraw(
bytes[] calldata pubKeys,
uint64[] calldata amounts,
uint256 maxFeePerWithdrawal,
address excessFeeRecipient
) external payable onlyOwnerOrRoles(WITHDRAWAL_ROLE) nonReentrant {
if (pubKeys.length != amounts.length) revert InvalidRequest_Params();
// check if the value sent is enough to cover the fees
_validateSufficientValueForFee(msg.value, maxFeePerWithdrawal * pubKeys.length);
// Check if fee exceeds maximum allowed, otherwise get fee
uint256 fee = _validateAndReturnFee(withdrawalSystemContract, maxFeePerWithdrawal);
uint256 totalFeePaid = 0;
for (uint256 i; i < pubKeys.length; i++) {
_validatePubkeyLength(pubKeys[i]);
// Add the withdrawal request
bytes memory callData = abi.encodePacked(pubKeys[i], amounts[i]);
(bool success, ) = withdrawalSystemContract.call{value: fee}(callData);
if (!success) revert InvalidWithdrawal_Failed();
totalFeePaid += fee;
// Emit withdrawal event for each validator
emit WithdrawalRequested(pubKeys[i], amounts[i], fee);
}
// Refund any excess value back to the excessFeeRecipient
_refundExcessFee(msg.value, totalFeePaid, excessFeeRecipient);
}
/// @inheritdoc IObolValidatorManager
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);
}
/// @inheritdoc IObolValidatorManager
function withdrawPullBalance(address account) external {
uint256 amount = pullBalances[account];
if (amount == 0) return;
unchecked {
// shouldn't underflow; fundsPendingWithdrawal = sum(pullBalances)
fundsPendingWithdrawal -= uint128(amount);
}
pullBalances[account] = 0;
account.safeTransferETH(amount);
emit PullBalanceWithdrawn(account, amount);
}
/// @inheritdoc IObolValidatorManager
function transfer(address newBeneficiary, address newOwner) external onlyOwner {
if (newBeneficiary == address(0)) revert InvalidRequest_Params();
principalRecipient = newBeneficiary;
emit BeneficiaryUpdated(newBeneficiary);
transferOwnership(newOwner);
emit Transferred(newBeneficiary, newOwner);
}
/// -----------------------------------------------------------------------
/// functions - view & pure
/// -----------------------------------------------------------------------
/// @inheritdoc IObolValidatorManager
function getPullBalance(address account) external view returns (uint256) {
return pullBalances[account];
}
/// @inheritdoc IObolValidatorManager
function getBeneficiary() external view returns (address) {
return principalRecipient;
}
/// @notice Retrieves the version of the contract.
/// @return Version of the contract
function version() external pure returns (string memory) {
return "1.0.0";
}
/// -----------------------------------------------------------------------
/// OwnableRoles function overrides
/// -----------------------------------------------------------------------
/// @inheritdoc IObolValidatorManager
function grantRoles(address user, uint256 roles) public payable override(IObolValidatorManager, OwnableRoles) {
super.grantRoles(user, roles);
}
/// @inheritdoc IObolValidatorManager
function revokeRoles(address user, uint256 roles) public payable override(IObolValidatorManager, OwnableRoles) {
super.revokeRoles(user, roles);
}
/// @inheritdoc IObolValidatorManager
function renounceRoles(uint256 roles) public payable override(IObolValidatorManager, OwnableRoles) {
super.renounceRoles(roles);
}
/// @inheritdoc IObolValidatorManager
function rolesOf(address user) public view override(IObolValidatorManager, OwnableRoles) returns (uint256 roles) {
return super.rolesOf(user);
}
/// @inheritdoc IObolValidatorManager
function hasAnyRole(
address user,
uint256 roles
) public view override(IObolValidatorManager, OwnableRoles) returns (bool) {
return super.hasAnyRole(user, roles);
}
/// @inheritdoc IObolValidatorManager
function hasAllRoles(
address user,
uint256 roles
) public view override(IObolValidatorManager, OwnableRoles) returns (bool) {
return super.hasAllRoles(user, roles);
}
/// @inheritdoc IObolValidatorManager
function transferOwnership(address newOwner) public payable override(IObolValidatorManager, Ownable) {
super.transferOwnership(newOwner);
}
/// @inheritdoc IObolValidatorManager
function renounceOwnership() public payable override(IObolValidatorManager, Ownable) {
super.renounceOwnership();
}
/// @inheritdoc IObolValidatorManager
function requestOwnershipHandover() public payable override(IObolValidatorManager, Ownable) {
super.requestOwnershipHandover();
}
/// @inheritdoc IObolValidatorManager
function cancelOwnershipHandover() public payable override(IObolValidatorManager, Ownable) {
super.cancelOwnershipHandover();
}
/// @inheritdoc IObolValidatorManager
function completeOwnershipHandover(address pendingOwner) public payable override(IObolValidatorManager, Ownable) {
super.completeOwnershipHandover(pendingOwner);
}
/// @inheritdoc IObolValidatorManager
function owner() public view override(IObolValidatorManager, Ownable) returns (address result) {
return super.owner();
}
/// @inheritdoc IObolValidatorManager
function ownershipHandoverExpiresAt(
address pendingOwner
) public view override(IObolValidatorManager, Ownable) returns (uint256 result) {
return super.ownershipHandoverExpiresAt(pendingOwner);
}
/// -----------------------------------------------------------------------
/// functions - private & internal
/// -----------------------------------------------------------------------
/// Internal function to validate the caller sent sufficient value for fee. Used for pectra related operations.
/// @param value The value.
/// @param totalFee The total fee.
function _validateSufficientValueForFee(uint256 value, uint256 totalFee) internal pure {
if (value < totalFee) revert InvalidRequest_NotEnoughFee();
}
/// Internal function to validate the fee. Used for pectra related operations.
/// @param feeContract The address of the fee contract.
/// @param maxAllowedFee The maximum allowed fee.
/// @return fee The fee.
/// @dev Reverts if the fee is higher than the maximum allowed fee, or if the fee read fails.
function _validateAndReturnFee(address feeContract, uint256 maxAllowedFee) internal view returns (uint256 fee) {
// Read current fee from the contract
(bool readOK, bytes memory feeData) = feeContract.staticcall("");
if (!readOK) revert InvalidRequest_SystemGetFee();
fee = uint256(bytes32(feeData));
if (fee > maxAllowedFee) revert InvalidRequest_NotEnoughFee();
}
/// Internal function to validate that a public key is exactly 48 bytes in length
/// @param pubkey The public key to validate
function _validatePubkeyLength(bytes memory pubkey) internal pure {
if (pubkey.length != PUBLIC_KEY_LENGTH) revert InvalidRequest_Params();
}
/// Internal function to refund the excess fee for pectra related operations.
/// @param _totalValueReceived The total value received.
/// @param _totalFeePaid The total fee paid.
/// @param _excessFeeRecipient The address of the excess fee recipient.
function _refundExcessFee(uint256 _totalValueReceived, uint256 _totalFeePaid, address _excessFeeRecipient) internal {
// send excess value back to _excessFeeRecipient
if (_totalValueReceived > _totalFeePaid) {
(bool success, ) = payable(_excessFeeRecipient).call{value: _totalValueReceived - _totalFeePaid}("");
if (!success) emit UnsentExcessFee(_excessFeeRecipient, _totalValueReceived - _totalFeePaid);
}
}
/// Distributes target token inside the contract to next-in-line 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);
emit PrincipalStakeAmountUpdated(amountOfPrincipalStake, amountOfPrincipalStake + _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)
if (pullOrPush == PULL) {
if (_principalPayout > 0 || _rewardPayout > 0) {
// Write to storage
fundsPendingWithdrawal = uint128(_memoryFundsPendingWithdrawal + _principalPayout + _rewardPayout);
}
}
// pay out principal
_payout(principalRecipient, _principalPayout, pullOrPush);
// pay out reward
_payout(rewardRecipient, _rewardPayout, pullOrPush);
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 beneficiary 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 beneficiary, 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
/// @param _ensName ENS name to register
/// @param _ensReverseRegistrar ENS reverse registrar address
/// @param _ensOwner ENS owner address
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 beneficiary Address to distribute principal payments to
/// @param rewardRecipient Address to distribute reward payments to
/// @param principalThreshold Principal vs rewards classification threshold (gwei),
/// the recommended value is 16000000000 (16 ether).
/// @return ovm Address of the new ObolValidatorManager instance
function createObolValidatorManager(
address owner,
address beneficiary,
address rewardRecipient,
uint64 principalThreshold
) external returns (ObolValidatorManager ovm) {
if (owner == address(0)) revert Invalid_Owner();
if (beneficiary == 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,
beneficiary,
rewardRecipient,
principalThreshold
);
emit CreateObolValidatorManager(address(ovm), owner, beneficiary, rewardRecipient, principalThreshold);
}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"_consolidationSystemContract","type":"address"},{"internalType":"address","name":"_withdrawalSystemContract","type":"address"},{"internalType":"address","name":"_depositSystemContract","type":"address"},{"internalType":"string","name":"_ensName","type":"string"},{"internalType":"address","name":"_ensReverseRegistrar","type":"address"},{"internalType":"address","name":"_ensOwner","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":"beneficiary","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":"beneficiary","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
60e06040523480156200001157600080fd5b50604051620047fb380380620047fb8339818101604052810190620000379190620002d7565b8573ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250508473ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508373ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff168152505050505050505062000392565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200012082620000f3565b9050919050565b620001328162000113565b81146200013e57600080fd5b50565b600081519050620001528162000127565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001ad8262000162565b810181811067ffffffffffffffff82111715620001cf57620001ce62000173565b5b80604052505050565b6000620001e4620000df565b9050620001f28282620001a2565b919050565b600067ffffffffffffffff82111562000215576200021462000173565b5b620002208262000162565b9050602081019050919050565b60005b838110156200024d57808201518184015260208101905062000230565b60008484015250505050565b6000620002706200026a84620001f7565b620001d8565b9050828152602081018484840111156200028f576200028e6200015d565b5b6200029c8482856200022d565b509392505050565b600082601f830112620002bc57620002bb62000158565b5b8151620002ce84826020860162000259565b91505092915050565b60008060008060008060c08789031215620002f757620002f6620000e9565b5b60006200030789828a0162000141565b96505060206200031a89828a0162000141565b95505060406200032d89828a0162000141565b945050606087015167ffffffffffffffff811115620003515762000350620000ee565b5b6200035f89828a01620002a4565b93505060806200037289828a0162000141565b92505060a06200038589828a0162000141565b9150509295509295509295565b60805160a05160c051614425620003d660003960008181610119015261031701526000818160f501526102f60152600081816102d501526103ef01526144256000f3fe60806040523480156200001157600080fd5b5060043610620000525760003560e01c806306dbd0cb14620000575780633e5b34b21462000079578063cdba577d146200009b578063f7f8ff7a14620000d1575b600080fd5b62000061620000f3565b60405162000070919062000464565b60405180910390f35b6200008362000117565b60405162000092919062000464565b60405180910390f35b620000b96004803603810190620000b39190620004fc565b6200013b565b604051620000c89190620005d9565b60405180910390f35b620000db620003ed565b604051620000ea919062000464565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603620001a3576040517f22adafa200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806200020b5750600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b1562000243576040517ffca23e2a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff160362000288576040517f8966ee9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6501dcd65000008267ffffffffffffffff161115620002d3576040517f12f9b82600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000087878787604051620003489062000411565b6200035a979695949392919062000607565b604051809103906000f08015801562000377573d6000803e3d6000fd5b5090508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f5f69e7dce622f5df4a1899cf39ca6eb1216397f2c07206d2ef33aebf960827a3868686604051620003dd9392919062000684565b60405180910390a3949350505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b613d2e80620006c283390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200044c826200041f565b9050919050565b6200045e816200043f565b82525050565b60006020820190506200047b600083018462000453565b92915050565b600080fd5b62000491816200043f565b81146200049d57600080fd5b50565b600081359050620004b18162000486565b92915050565b600067ffffffffffffffff82169050919050565b620004d681620004b7565b8114620004e257600080fd5b50565b600081359050620004f681620004cb565b92915050565b6000806000806080858703121562000519576200051862000481565b5b60006200052987828801620004a0565b94505060206200053c87828801620004a0565b93505060406200054f87828801620004a0565b92505060606200056287828801620004e5565b91505092959194509250565b6000819050919050565b600062000599620005936200058d846200041f565b6200056e565b6200041f565b9050919050565b6000620005ad8262000578565b9050919050565b6000620005c182620005a0565b9050919050565b620005d381620005b4565b82525050565b6000602082019050620005f06000830184620005c8565b92915050565b6200060181620004b7565b82525050565b600060e0820190506200061e600083018a62000453565b6200062d602083018962000453565b6200063c604083018862000453565b6200064b606083018762000453565b6200065a608083018662000453565b6200066960a083018562000453565b6200067860c0830184620005f6565b98975050505050505050565b60006060820190506200069b600083018662000453565b620006aa602083018562000453565b620006b96040830184620005f6565b94935050505056fe61010060405260016000553480156200001757600080fd5b5060405162003d2e38038062003d2e83398181016040528101906200003d9190620004ea565b600073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1603620000a4576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16036200010b576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff160362000172576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603620001d9576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160362000240576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620002a7576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250508573ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508473ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff168152505082600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508067ffffffffffffffff1660e08167ffffffffffffffff1681525050620003f284620003ff60201b60201c565b505050505050506200059d565b8060601b60601c905080638b78c6d819558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a350565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200046d8262000440565b9050919050565b6200047f8162000460565b81146200048b57600080fd5b50565b6000815190506200049f8162000474565b92915050565b600067ffffffffffffffff82169050919050565b620004c481620004a5565b8114620004d057600080fd5b50565b600081519050620004e481620004b9565b92915050565b600080600080600080600060e0888a0312156200050c576200050b6200043b565b5b60006200051c8a828b016200048e565b97505060206200052f8a828b016200048e565b9650506040620005428a828b016200048e565b9550506060620005558a828b016200048e565b9450506080620005688a828b016200048e565b93505060a06200057b8a828b016200048e565b92505060c06200058e8a828b01620004d3565b91505092959891949750929550565b60805160a05160c05160e05161372d620006016000396000818161135301526121a5015260008181610c620152610f6d0152600081816109590152818161143a015261155601526000818161190701528181611c820152611fea015261372d6000f3fe6080604052600436106102605760003560e01c806354d1f13d116101445780639124aafb116100b6578063e521136f1161007a578063e521136f146107fa578063f04e283e14610823578063f2fde38b1461083f578063f5e45d4d1461085b578063f7f8ff7a14610884578063fee81cf4146108af57610267565b80639124aafb146107455780639fdb5e8a14610761578063ba45b0b81461078c578063bca2da78146107b5578063dc04d8f4146107de57610267565b80636c196886116101085780636c196886146106665780636ea056a914610691578063715018a6146106ba57806379de7638146106c45780637bfafd59146106ef5780638da5cb5b1461071a57610267565b806354d1f13d146105b057806354fd4d50146105ba578063565a2e2c146105e55780635f097d751461061057806367db90c21461063b57610267565b806324ae6a27116101dd578063353efdcf116101a1578063353efdcf146104bf578063377b2c34146104ea5780633a6a4d2e146105155780633e5b34b21461052c5780634a4ee7b114610557578063514e62fc1461057357610267565b806324ae6a271461040d57806325692962146104365780632d7fd507146104405780632de94807146104575780633332c3241461049457610267565b8063183a4f6e11610224578063183a4f6e146103535780631c10893f1461036f5780631c31f7101461038b5780631cd64df4146103b457806322895118146103f157610267565b806303615ba31461026c578063043edae8146102a957806306dbd0cb146102d45780630da58dbb146102ff57806317f333401461032857610267565b3661026757005b600080fd5b34801561027857600080fd5b50610293600480360381019061028e919061298c565b6108ec565b6040516102a091906129d2565b60405180910390f35b3480156102b557600080fd5b506102be610935565b6040516102cb9190612a18565b60405180910390f35b3480156102e057600080fd5b506102e9610957565b6040516102f69190612a42565b60405180910390f35b34801561030b57600080fd5b506103266004803603810190610321919061298c565b61097b565b005b34801561033457600080fd5b5061033d610ae8565b60405161034a9190612a42565b60405180910390f35b61036d60048036038101906103689190612a89565b610b0e565b005b61038960048036038101906103849190612ab6565b610b1a565b005b34801561039757600080fd5b506103b260048036038101906103ad919061298c565b610b28565b005b3480156103c057600080fd5b506103db60048036038101906103d69190612ab6565b610c21565b6040516103e89190612b11565b60405180910390f35b61040b60048036038101906104069190612bc7565b610c35565b005b34801561041957600080fd5b50610434600480360381019061042f9190612c90565b610d3e565b005b61043e610e5d565b005b34801561044c57600080fd5b50610455610e67565b005b34801561046357600080fd5b5061047e6004803603810190610479919061298c565b610ec8565b60405161048b91906129d2565b60405180910390f35b3480156104a057600080fd5b506104a9610eda565b6040516104b691906129d2565b60405180910390f35b3480156104cb57600080fd5b506104d4610edf565b6040516104e191906129d2565b60405180910390f35b3480156104f657600080fd5b506104ff610ee4565b60405161050c9190612a42565b60405180910390f35b34801561052157600080fd5b5061052a610f0a565b005b34801561053857600080fd5b50610541610f6b565b60405161054e9190612a42565b60405180910390f35b610571600480360381019061056c9190612ab6565b610f8f565b005b34801561057f57600080fd5b5061059a60048036038101906105959190612ab6565b610f9d565b6040516105a79190612b11565b60405180910390f35b6105b8610fb1565b005b3480156105c657600080fd5b506105cf610fbb565b6040516105dc9190612d60565b60405180910390f35b3480156105f157600080fd5b506105fa610ff8565b6040516106079190612a42565b60405180910390f35b34801561061c57600080fd5b50610625611022565b60405161063291906129d2565b60405180910390f35b34801561064757600080fd5b50610650611028565b60405161065d91906129d2565b60405180910390f35b34801561067257600080fd5b5061067b61102d565b60405161068891906129d2565b60405180910390f35b34801561069d57600080fd5b506106b860048036038101906106b39190612ab6565b611032565b005b6106c2611342565b005b3480156106d057600080fd5b506106d961134c565b6040516106e691906129d2565b60405180910390f35b3480156106fb57600080fd5b50610704611351565b6040516107119190612da5565b60405180910390f35b34801561072657600080fd5b5061072f611375565b60405161073c9190612a42565b60405180910390f35b61075f600480360381019061075a9190612e6c565b611384565b005b34801561076d57600080fd5b506107766116e8565b60405161078391906129d2565b60405180910390f35b34801561079857600080fd5b506107b360048036038101906107ae9190612c90565b6116ed565b005b3480156107c157600080fd5b506107dc60048036038101906107d79190612a89565b611846565b005b6107f860048036038101906107f39190612f69565b6118a8565b005b34801561080657600080fd5b50610821600480360381019061081c919061298c565b611e4f565b005b61083d6004803603810190610838919061298c565b611f48565b005b6108596004803603810190610854919061298c565b611f54565b005b34801561086757600080fd5b50610882600480360381019061087d9190612ab6565b611f60565b005b34801561089057600080fd5b50610899611fe8565b6040516108a69190612a42565b60405180910390f35b3480156108bb57600080fd5b506108d660048036038101906108d1919061298c565b61200c565b6040516108e391906129d2565b60405180910390f35b6000600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600460009054906101000a90046fffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600081036109cd5750610ae5565b80600460008282829054906101000a90046fffffffffffffffffffffffffffffffff160392506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610a95818373ffffffffffffffffffffffffffffffffffffffff1661201e90919063ffffffff16565b8173ffffffffffffffffffffffffffffffffffffffff167fcbfdaa15c344a7a5b846b2199db1ca289c82041c65048ce19264f9fdd8cee27482604051610adb91906129d2565b60405180910390a2505b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610b178161203e565b50565b610b24828261204b565b5050565b6004610b3381612061565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610b99576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167feee59a71c694e68368a1cb0d135c448051bbfb12289e6c2223b0ceb100c2321d60405160405180910390a25050565b6000610c2d8383612098565b905092915050565b6020610c4081612061565b600060035490503460036000828254610c59919061300c565b925050819055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166322895118348b8b8b8b8b8b8b6040518963ffffffff1660e01b8152600401610cc6979695949392919061309c565b6000604051808303818588803b158015610cdf57600080fd5b505af1158015610cf3573d6000803e3d6000fd5b50505050507f09aced8c098d4a0a1f486296935d5746bc64dfd41ed58ad84f4e5afab784fcf860035482604051610d2b9291906130fc565b60405180910390a1505050505050505050565b6008610d4981612061565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610d849190612a42565b602060405180830381865afa158015610da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc5919061313a565b9050610df283828673ffffffffffffffffffffffffffffffffffffffff166120af9092919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f02dc46f45168b3833fa84aa22ee90f4ffec812cf1067be0413353eb95c031b0583604051610e4f91906129d2565b60405180910390a350505050565b610e656120fe565b565b600160005414610eac576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ea3906131b3565b60405180910390fd5b6002600081905550610ebe6001612152565b6001600081905550565b6000610ed3826123f0565b9050919050565b600481565b602081565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160005414610f4f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f46906131b3565b60405180910390fd5b6002600081905550610f616000612152565b6001600081905550565b7f000000000000000000000000000000000000000000000000000000000000000081565b610f99828261240b565b5050565b6000610fa98383612421565b905092915050565b610fb9612439565b565b60606040518060400160405280600581526020017f312e302e30000000000000000000000000000000000000000000000000000000815250905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60035481565b600181565b600281565b600160005414611077576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161106e906131b3565b60405180910390fd5b60026000819055506000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146110e6576110e2612475565b8290505b60008083146110f55782611158565b60056000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020545b905060056000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548111156111f5576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008282829054906101000a90046fffffffffffffffffffffffffffffffff160392506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508060056000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546112c091906131d3565b92505081905550808273ffffffffffffffffffffffffffffffffffffffff167fc36b5179cb9c303b200074996eab2b3473eac370fdd7eba3bec636fe3510969660405160405180910390a3611334818373ffffffffffffffffffffffffffffffffffffffff1661201e90919063ffffffff16565b505060016000819055505050565b61134a612492565b565b601081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600061137f6124a6565b905090565b600161138f81612061565b6001600054146113d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113cb906131b3565b60405180910390fd5b600260008190555084849050878790501461141b576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61143334888890508561142e9190613207565b6124b4565b600061145f7f0000000000000000000000000000000000000000000000000000000000000000856124f2565b90506000805b898990508110156116c9576114e08a8a8381811061148657611485613249565b5b90506020028101906114989190613287565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506125e7565b60008a8a838181106114f5576114f4613249565b5b90506020028101906115079190613287565b8a8a8581811061151a57611519613249565b5b905060200201602081019061152f9190613316565b604051602001611541939291906133a9565b604051602081830303815290604052905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16858360405161159a919061340f565b60006040518083038185875af1925050503d80600081146115d7576040519150601f19603f3d011682016040523d82523d6000602084013e6115dc565b606091505b5050905080611617576040517fd3d2fd8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8484611623919061300c565b9350848a8a8581811061163957611638613249565b5b905060200201602081019061164e9190613316565b67ffffffffffffffff167f19cd6ffa1afc5a20b44dd0fea708b8fb38af1d7891d4929811a3c65ff1624fb98e8e8781811061168c5761168b613249565b5b905060200281019061169e9190613287565b6040516116ac929190613426565b60405180910390a3505080806116c19061344a565b915050611465565b506116d5348286612625565b5050600160008190555050505050505050565b600881565b6116f5612475565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361175b576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167feee59a71c694e68368a1cb0d135c448051bbfb12289e6c2223b0ceb100c2321d60405160405180910390a26117e881611f54565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fa1c3c7ba08cdab9542dfbb1f9606093e828bca6c2678c82942703ec4e223790260405160405180910390a35050565b600461185181612061565b6003548203156118a45760006003549050826003819055507f09aced8c098d4a0a1f486296935d5746bc64dfd41ed58ad84f4e5afab784fcf8838260405161189a9291906130fc565b60405180910390a1505b5050565b60026118b381612061565b6001600054146118f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118ef906131b3565b60405180910390fd5b6002600081905550600061192c7f0000000000000000000000000000000000000000000000000000000000000000856124f2565b90506000805b87879050811015611a4357600088888381811061195257611951613249565b5b90506020028101906119649190613492565b806000019061197391906134ba565b905014806119b65750603f88888381811061199157611990613249565b5b90506020028101906119a39190613492565b80600001906119b291906134ba565b9050115b156119ed576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878782818110611a00576119ff613249565b5b9050602002810190611a129190613492565b8060000190611a2191906134ba565b905082611a2e919061300c565b91508080611a3b9061344a565b915050611932565b5060008183611a529190613207565b9050611a5e34826124b4565b60005b88889050811015611e3157611aeb898983818110611a8257611a81613249565b5b9050602002810190611a949190613492565b8060200190611aa39190613287565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506125e7565b60005b898983818110611b0157611b00613249565b5b9050602002810190611b139190613492565b8060000190611b2291906134ba565b9050811015611e1d57611bcd8a8a84818110611b4157611b40613249565b5b9050602002810190611b539190613492565b8060000190611b6291906134ba565b83818110611b7357611b72613249565b5b9050602002810190611b859190613287565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506125e7565b60008a8a84818110611be257611be1613249565b5b9050602002810190611bf49190613492565b8060000190611c0391906134ba565b83818110611c1457611c13613249565b5b9050602002810190611c269190613287565b8c8c86818110611c3957611c38613249565b5b9050602002810190611c4b9190613492565b8060200190611c5a9190613287565b604051602001611c6d949392919061351d565b604051602081830303815290604052905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168783604051611cc6919061340f565b60006040518083038185875af1925050503d8060008114611d03576040519150601f19603f3d011682016040523d82523d6000602084013e611d08565b606091505b5050905080611d43576040517f1684b73e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b867f70327b6e1280f8015a284fca27cbbef10ac24c017f6452ff98212167813459d88d8d87818110611d7857611d77613249565b5b9050602002810190611d8a9190613492565b8060000190611d9991906134ba565b86818110611daa57611da9613249565b5b9050602002810190611dbc9190613287565b8f8f89818110611dcf57611dce613249565b5b9050602002810190611de19190613492565b8060200190611df09190613287565b604051611e009493929190613545565b60405180910390a250508080611e159061344a565b915050611aee565b508080611e299061344a565b915050611a61565b50611e3d348287612625565b50505060016000819055505050505050565b6010611e5a81612061565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611ec0576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167fab0699d690a6fcb85f70e4d28ee4c6114c3d90c12c579ca5abe089b8ff6ff1bf60405160405180910390a25050565b611f5181612700565b50565b611f5d81612741565b50565b600160005414611fa5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f9c906131b3565b60405180910390fd5b60026000819055506040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fdf906135cc565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000081565b60006120178261276b565b9050919050565b60005a60005a84865af161203a5763b12d13eb6000526004601cfd5b5050565b6120483382612786565b50565b612053612475565b61205d8282612796565b5050565b638b78c6d81954331461209557638b78c6d8600c5233600052806020600c205416612094576382b429006000526004601cfd5b5b50565b600081826120a585610ec8565b1614905092915050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d1560016000511417166120f4576390b8ec186000526004601cfd5b6000603452505050565b60006121086127a6565b67ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b60004790506000600460009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1690506000818361219a91906131d3565b90506000633b9aca007f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff166121d89190613207565b90506000808284101580156121ef57506000600354115b156122185760035484111561220f57600354915060035484039050612213565b8391505b61221c565b8390505b6fffffffffffffffffffffffffffffffff8016841115612268576040517f502cbcb200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008211156122e557816fffffffffffffffffffffffffffffffff166003600082825461229591906131d3565b925050819055507f09aced8c098d4a0a1f486296935d5746bc64dfd41ed58ad84f4e5afab784fcf8600354836003546122ce919061300c565b6040516122dc9291906130fc565b60405180910390a15b600187036123525760008211806122fc5750600081115b156123515780828661230e919061300c565b612318919061300c565b600460006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b5b61237f600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683896127b1565b6123ac600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682896127b1565b7f8b367b31582d5c638694615c1cfc77ffcf9febcf6e6c793358089f7769cfeee68282896040516123df939291906135ec565b60405180910390a150505050505050565b6000638b78c6d8600c52816000526020600c20549050919050565b612413612475565b61241d8282612786565b5050565b6000808261242e85610ec8565b161415905092915050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b638b78c6d819543314612490576382b429006000526004601cfd5b565b61249a612475565b6124a4600061288c565b565b6000638b78c6d81954905090565b808210156124ee576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60008060008473ffffffffffffffffffffffffffffffffffffffff1660405161251a90613649565b600060405180830381855afa9150503d8060008114612555576040519150601f19603f3d011682016040523d82523d6000602084013e61255a565b606091505b509150915081612596576040517f448e5c4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806125a090613690565b60001c9250838311156125df576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b6030815114612622576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b818311156126fb5760008173ffffffffffffffffffffffffffffffffffffffff16838561265291906131d3565b60405161265e90613649565b60006040518083038185875af1925050503d806000811461269b576040519150601f19603f3d011682016040523d82523d6000602084013e6126a0565b606091505b50509050806126f95782846126b591906131d3565b8273ffffffffffffffffffffffffffffffffffffffff167fff295b6ae91fb82f39d5bb544f1e62ce33f6978255b7c7434f8137bdf107f6d960405160405180910390a35b505b505050565b612708612475565b63389a75e1600c52806000526020600c20805442111561273057636f5e88186000526004601cfd5b600081555061273e8161288c565b50565b612749612475565b8060601b61275f57637448fbae6000526004601cfd5b6127688161288c565b50565b600063389a75e1600c52816000526020600c20549050919050565b612792828260006128ca565b5050565b6127a2828260016128ca565b5050565b60006202a300905090565b6000821115612887576001810361281d5781600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612811919061300c565b92505081905550612886565b600081036128535761284e828473ffffffffffffffffffffffffffffffffffffffff1661201e90919063ffffffff16565b612885565b6040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5b505050565b638b78c6d8198160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a38181555050565b638b78c6d8600c52826000526020600c208054838117836128ed57848216821890505b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3505050505050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006129598261292e565b9050919050565b6129698161294e565b811461297457600080fd5b50565b60008135905061298681612960565b92915050565b6000602082840312156129a2576129a1612924565b5b60006129b084828501612977565b91505092915050565b6000819050919050565b6129cc816129b9565b82525050565b60006020820190506129e760008301846129c3565b92915050565b60006fffffffffffffffffffffffffffffffff82169050919050565b612a12816129ed565b82525050565b6000602082019050612a2d6000830184612a09565b92915050565b612a3c8161294e565b82525050565b6000602082019050612a576000830184612a33565b92915050565b612a66816129b9565b8114612a7157600080fd5b50565b600081359050612a8381612a5d565b92915050565b600060208284031215612a9f57612a9e612924565b5b6000612aad84828501612a74565b91505092915050565b60008060408385031215612acd57612acc612924565b5b6000612adb85828601612977565b9250506020612aec85828601612a74565b9150509250929050565b60008115159050919050565b612b0b81612af6565b82525050565b6000602082019050612b266000830184612b02565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112612b5157612b50612b2c565b5b8235905067ffffffffffffffff811115612b6e57612b6d612b31565b5b602083019150836001820283011115612b8a57612b89612b36565b5b9250929050565b6000819050919050565b612ba481612b91565b8114612baf57600080fd5b50565b600081359050612bc181612b9b565b92915050565b60008060008060008060006080888a031215612be657612be5612924565b5b600088013567ffffffffffffffff811115612c0457612c03612929565b5b612c108a828b01612b3b565b9750975050602088013567ffffffffffffffff811115612c3357612c32612929565b5b612c3f8a828b01612b3b565b9550955050604088013567ffffffffffffffff811115612c6257612c61612929565b5b612c6e8a828b01612b3b565b93509350506060612c818a828b01612bb2565b91505092959891949750929550565b60008060408385031215612ca757612ca6612924565b5b6000612cb585828601612977565b9250506020612cc685828601612977565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b60005b83811015612d0a578082015181840152602081019050612cef565b60008484015250505050565b6000601f19601f8301169050919050565b6000612d3282612cd0565b612d3c8185612cdb565b9350612d4c818560208601612cec565b612d5581612d16565b840191505092915050565b60006020820190508181036000830152612d7a8184612d27565b905092915050565b600067ffffffffffffffff82169050919050565b612d9f81612d82565b82525050565b6000602082019050612dba6000830184612d96565b92915050565b60008083601f840112612dd657612dd5612b2c565b5b8235905067ffffffffffffffff811115612df357612df2612b31565b5b602083019150836020820283011115612e0f57612e0e612b36565b5b9250929050565b60008083601f840112612e2c57612e2b612b2c565b5b8235905067ffffffffffffffff811115612e4957612e48612b31565b5b602083019150836020820283011115612e6557612e64612b36565b5b9250929050565b60008060008060008060808789031215612e8957612e88612924565b5b600087013567ffffffffffffffff811115612ea757612ea6612929565b5b612eb389828a01612dc0565b9650965050602087013567ffffffffffffffff811115612ed657612ed5612929565b5b612ee289828a01612e16565b94509450506040612ef589828a01612a74565b9250506060612f0689828a01612977565b9150509295509295509295565b60008083601f840112612f2957612f28612b2c565b5b8235905067ffffffffffffffff811115612f4657612f45612b31565b5b602083019150836020820283011115612f6257612f61612b36565b5b9250929050565b60008060008060608587031215612f8357612f82612924565b5b600085013567ffffffffffffffff811115612fa157612fa0612929565b5b612fad87828801612f13565b94509450506020612fc087828801612a74565b9250506040612fd187828801612977565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000613017826129b9565b9150613022836129b9565b925082820190508082111561303a57613039612fdd565b5b92915050565b600082825260208201905092915050565b82818337600083830152505050565b600061306c8385613040565b9350613079838584613051565b61308283612d16565b840190509392505050565b61309681612b91565b82525050565b600060808201905081810360008301526130b781898b613060565b905081810360208301526130cc818789613060565b905081810360408301526130e1818587613060565b90506130f0606083018461308d565b98975050505050505050565b600060408201905061311160008301856129c3565b61311e60208301846129c3565b9392505050565b60008151905061313481612a5d565b92915050565b6000602082840312156131505761314f612924565b5b600061315e84828501613125565b91505092915050565b7f5245454e5452414e435900000000000000000000000000000000000000000000600082015250565b600061319d600a83612cdb565b91506131a882613167565b602082019050919050565b600060208201905081810360008301526131cc81613190565b9050919050565b60006131de826129b9565b91506131e9836129b9565b925082820390508181111561320157613200612fdd565b5b92915050565b6000613212826129b9565b915061321d836129b9565b925082820261322b816129b9565b9150828204841483151761324257613241612fdd565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126132a4576132a3613278565b5b80840192508235915067ffffffffffffffff8211156132c6576132c561327d565b5b6020830192506001820236038313156132e2576132e1613282565b5b509250929050565b6132f381612d82565b81146132fe57600080fd5b50565b600081359050613310816132ea565b92915050565b60006020828403121561332c5761332b612924565b5b600061333a84828501613301565b91505092915050565b600081905092915050565b600061335a8385613343565b9350613367838584613051565b82840190509392505050565b60008160c01b9050919050565b600061338b82613373565b9050919050565b6133a361339e82612d82565b613380565b82525050565b60006133b682858761334e565b91506133c28284613392565b600882019150819050949350505050565b600081519050919050565b60006133e9826133d3565b6133f38185613343565b9350613403818560208601612cec565b80840191505092915050565b600061341b82846133de565b915081905092915050565b60006020820190508181036000830152613441818486613060565b90509392505050565b6000613455826129b9565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361348757613486612fdd565b5b600182019050919050565b6000823560016040038336030381126134ae576134ad613278565b5b80830191505092915050565b600080833560016020038436030381126134d7576134d6613278565b5b80840192508235915067ffffffffffffffff8211156134f9576134f861327d565b5b60208301925060208202360383131561351557613514613282565b5b509250929050565b600061352a82868861334e565b915061353782848661334e565b915081905095945050505050565b60006040820190508181036000830152613560818688613060565b90508181036020830152613575818486613060565b905095945050505050565b7f4e6f7420696d706c656d656e7465640000000000000000000000000000000000600082015250565b60006135b6600f83612cdb565b91506135c182613580565b602082019050919050565b600060208201905081810360008301526135e5816135a9565b9050919050565b600060608201905061360160008301866129c3565b61360e60208301856129c3565b61361b60408301846129c3565b949350505050565b50565b6000613633600083613343565b915061363e82613623565b600082019050919050565b600061365482613626565b9150819050919050565b6000819050602082019050919050565b600061367a8251612b91565b80915050919050565b600082821b905092915050565b600061369b826133d3565b826136a58461365e565b90506136b08161366e565b925060208210156136f0576136eb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802613683565b831692505b505091905056fea26469706673582212205b83464c8159b4a300316c2a0b8a5da2e7cc180d9e2495b4c3be8d1a402389a864736f6c63430008130033a2646970667358221220aea92119bec81712bf6fc14aab2158d1db00f01c70decc4c8ae0f54e5391b91364736f6c634300081300330000000000000000000000000000bbddc7ce488642fb579f8b00f3a59000725100000000000000000000000000000961ef480eb55e80d19ad83579a64c00700200000000000000000000000000000000219ab540356cbb839cbe05303d7705fa00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4f564d342e466163746f72790000000000000000000000000000000000000000
Deployed Bytecode
0x60806040523480156200001157600080fd5b5060043610620000525760003560e01c806306dbd0cb14620000575780633e5b34b21462000079578063cdba577d146200009b578063f7f8ff7a14620000d1575b600080fd5b62000061620000f3565b60405162000070919062000464565b60405180910390f35b6200008362000117565b60405162000092919062000464565b60405180910390f35b620000b96004803603810190620000b39190620004fc565b6200013b565b604051620000c89190620005d9565b60405180910390f35b620000db620003ed565b604051620000ea919062000464565b60405180910390f35b7f00000000000000000000000000000961ef480eb55e80d19ad83579a64c00700281565b7f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa81565b60008073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603620001a3576040517f22adafa200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806200020b5750600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b1562000243576040517ffca23e2a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff160362000288576040517f8966ee9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6501dcd65000008267ffffffffffffffff161115620002d3576040517f12f9b82600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000bbddc7ce488642fb579f8b00f3a5900072517f00000000000000000000000000000961ef480eb55e80d19ad83579a64c0070027f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa87878787604051620003489062000411565b6200035a979695949392919062000607565b604051809103906000f08015801562000377573d6000803e3d6000fd5b5090508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f5f69e7dce622f5df4a1899cf39ca6eb1216397f2c07206d2ef33aebf960827a3868686604051620003dd9392919062000684565b60405180910390a3949350505050565b7f0000000000000000000000000000bbddc7ce488642fb579f8b00f3a59000725181565b613d2e80620006c283390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200044c826200041f565b9050919050565b6200045e816200043f565b82525050565b60006020820190506200047b600083018462000453565b92915050565b600080fd5b62000491816200043f565b81146200049d57600080fd5b50565b600081359050620004b18162000486565b92915050565b600067ffffffffffffffff82169050919050565b620004d681620004b7565b8114620004e257600080fd5b50565b600081359050620004f681620004cb565b92915050565b6000806000806080858703121562000519576200051862000481565b5b60006200052987828801620004a0565b94505060206200053c87828801620004a0565b93505060406200054f87828801620004a0565b92505060606200056287828801620004e5565b91505092959194509250565b6000819050919050565b600062000599620005936200058d846200041f565b6200056e565b6200041f565b9050919050565b6000620005ad8262000578565b9050919050565b6000620005c182620005a0565b9050919050565b620005d381620005b4565b82525050565b6000602082019050620005f06000830184620005c8565b92915050565b6200060181620004b7565b82525050565b600060e0820190506200061e600083018a62000453565b6200062d602083018962000453565b6200063c604083018862000453565b6200064b606083018762000453565b6200065a608083018662000453565b6200066960a083018562000453565b6200067860c0830184620005f6565b98975050505050505050565b60006060820190506200069b600083018662000453565b620006aa602083018562000453565b620006b96040830184620005f6565b94935050505056fe61010060405260016000553480156200001757600080fd5b5060405162003d2e38038062003d2e83398181016040528101906200003d9190620004ea565b600073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1603620000a4576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16036200010b576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff160362000172576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603620001d9576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160362000240576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620002a7576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8673ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250508573ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508473ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff168152505082600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508067ffffffffffffffff1660e08167ffffffffffffffff1681525050620003f284620003ff60201b60201c565b505050505050506200059d565b8060601b60601c905080638b78c6d819558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a350565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200046d8262000440565b9050919050565b6200047f8162000460565b81146200048b57600080fd5b50565b6000815190506200049f8162000474565b92915050565b600067ffffffffffffffff82169050919050565b620004c481620004a5565b8114620004d057600080fd5b50565b600081519050620004e481620004b9565b92915050565b600080600080600080600060e0888a0312156200050c576200050b6200043b565b5b60006200051c8a828b016200048e565b97505060206200052f8a828b016200048e565b9650506040620005428a828b016200048e565b9550506060620005558a828b016200048e565b9450506080620005688a828b016200048e565b93505060a06200057b8a828b016200048e565b92505060c06200058e8a828b01620004d3565b91505092959891949750929550565b60805160a05160c05160e05161372d620006016000396000818161135301526121a5015260008181610c620152610f6d0152600081816109590152818161143a015261155601526000818161190701528181611c820152611fea015261372d6000f3fe6080604052600436106102605760003560e01c806354d1f13d116101445780639124aafb116100b6578063e521136f1161007a578063e521136f146107fa578063f04e283e14610823578063f2fde38b1461083f578063f5e45d4d1461085b578063f7f8ff7a14610884578063fee81cf4146108af57610267565b80639124aafb146107455780639fdb5e8a14610761578063ba45b0b81461078c578063bca2da78146107b5578063dc04d8f4146107de57610267565b80636c196886116101085780636c196886146106665780636ea056a914610691578063715018a6146106ba57806379de7638146106c45780637bfafd59146106ef5780638da5cb5b1461071a57610267565b806354d1f13d146105b057806354fd4d50146105ba578063565a2e2c146105e55780635f097d751461061057806367db90c21461063b57610267565b806324ae6a27116101dd578063353efdcf116101a1578063353efdcf146104bf578063377b2c34146104ea5780633a6a4d2e146105155780633e5b34b21461052c5780634a4ee7b114610557578063514e62fc1461057357610267565b806324ae6a271461040d57806325692962146104365780632d7fd507146104405780632de94807146104575780633332c3241461049457610267565b8063183a4f6e11610224578063183a4f6e146103535780631c10893f1461036f5780631c31f7101461038b5780631cd64df4146103b457806322895118146103f157610267565b806303615ba31461026c578063043edae8146102a957806306dbd0cb146102d45780630da58dbb146102ff57806317f333401461032857610267565b3661026757005b600080fd5b34801561027857600080fd5b50610293600480360381019061028e919061298c565b6108ec565b6040516102a091906129d2565b60405180910390f35b3480156102b557600080fd5b506102be610935565b6040516102cb9190612a18565b60405180910390f35b3480156102e057600080fd5b506102e9610957565b6040516102f69190612a42565b60405180910390f35b34801561030b57600080fd5b506103266004803603810190610321919061298c565b61097b565b005b34801561033457600080fd5b5061033d610ae8565b60405161034a9190612a42565b60405180910390f35b61036d60048036038101906103689190612a89565b610b0e565b005b61038960048036038101906103849190612ab6565b610b1a565b005b34801561039757600080fd5b506103b260048036038101906103ad919061298c565b610b28565b005b3480156103c057600080fd5b506103db60048036038101906103d69190612ab6565b610c21565b6040516103e89190612b11565b60405180910390f35b61040b60048036038101906104069190612bc7565b610c35565b005b34801561041957600080fd5b50610434600480360381019061042f9190612c90565b610d3e565b005b61043e610e5d565b005b34801561044c57600080fd5b50610455610e67565b005b34801561046357600080fd5b5061047e6004803603810190610479919061298c565b610ec8565b60405161048b91906129d2565b60405180910390f35b3480156104a057600080fd5b506104a9610eda565b6040516104b691906129d2565b60405180910390f35b3480156104cb57600080fd5b506104d4610edf565b6040516104e191906129d2565b60405180910390f35b3480156104f657600080fd5b506104ff610ee4565b60405161050c9190612a42565b60405180910390f35b34801561052157600080fd5b5061052a610f0a565b005b34801561053857600080fd5b50610541610f6b565b60405161054e9190612a42565b60405180910390f35b610571600480360381019061056c9190612ab6565b610f8f565b005b34801561057f57600080fd5b5061059a60048036038101906105959190612ab6565b610f9d565b6040516105a79190612b11565b60405180910390f35b6105b8610fb1565b005b3480156105c657600080fd5b506105cf610fbb565b6040516105dc9190612d60565b60405180910390f35b3480156105f157600080fd5b506105fa610ff8565b6040516106079190612a42565b60405180910390f35b34801561061c57600080fd5b50610625611022565b60405161063291906129d2565b60405180910390f35b34801561064757600080fd5b50610650611028565b60405161065d91906129d2565b60405180910390f35b34801561067257600080fd5b5061067b61102d565b60405161068891906129d2565b60405180910390f35b34801561069d57600080fd5b506106b860048036038101906106b39190612ab6565b611032565b005b6106c2611342565b005b3480156106d057600080fd5b506106d961134c565b6040516106e691906129d2565b60405180910390f35b3480156106fb57600080fd5b50610704611351565b6040516107119190612da5565b60405180910390f35b34801561072657600080fd5b5061072f611375565b60405161073c9190612a42565b60405180910390f35b61075f600480360381019061075a9190612e6c565b611384565b005b34801561076d57600080fd5b506107766116e8565b60405161078391906129d2565b60405180910390f35b34801561079857600080fd5b506107b360048036038101906107ae9190612c90565b6116ed565b005b3480156107c157600080fd5b506107dc60048036038101906107d79190612a89565b611846565b005b6107f860048036038101906107f39190612f69565b6118a8565b005b34801561080657600080fd5b50610821600480360381019061081c919061298c565b611e4f565b005b61083d6004803603810190610838919061298c565b611f48565b005b6108596004803603810190610854919061298c565b611f54565b005b34801561086757600080fd5b50610882600480360381019061087d9190612ab6565b611f60565b005b34801561089057600080fd5b50610899611fe8565b6040516108a69190612a42565b60405180910390f35b3480156108bb57600080fd5b506108d660048036038101906108d1919061298c565b61200c565b6040516108e391906129d2565b60405180910390f35b6000600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600460009054906101000a90046fffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600081036109cd5750610ae5565b80600460008282829054906101000a90046fffffffffffffffffffffffffffffffff160392506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610a95818373ffffffffffffffffffffffffffffffffffffffff1661201e90919063ffffffff16565b8173ffffffffffffffffffffffffffffffffffffffff167fcbfdaa15c344a7a5b846b2199db1ca289c82041c65048ce19264f9fdd8cee27482604051610adb91906129d2565b60405180910390a2505b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610b178161203e565b50565b610b24828261204b565b5050565b6004610b3381612061565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610b99576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167feee59a71c694e68368a1cb0d135c448051bbfb12289e6c2223b0ceb100c2321d60405160405180910390a25050565b6000610c2d8383612098565b905092915050565b6020610c4081612061565b600060035490503460036000828254610c59919061300c565b925050819055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166322895118348b8b8b8b8b8b8b6040518963ffffffff1660e01b8152600401610cc6979695949392919061309c565b6000604051808303818588803b158015610cdf57600080fd5b505af1158015610cf3573d6000803e3d6000fd5b50505050507f09aced8c098d4a0a1f486296935d5746bc64dfd41ed58ad84f4e5afab784fcf860035482604051610d2b9291906130fc565b60405180910390a1505050505050505050565b6008610d4981612061565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610d849190612a42565b602060405180830381865afa158015610da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc5919061313a565b9050610df283828673ffffffffffffffffffffffffffffffffffffffff166120af9092919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f02dc46f45168b3833fa84aa22ee90f4ffec812cf1067be0413353eb95c031b0583604051610e4f91906129d2565b60405180910390a350505050565b610e656120fe565b565b600160005414610eac576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ea3906131b3565b60405180910390fd5b6002600081905550610ebe6001612152565b6001600081905550565b6000610ed3826123f0565b9050919050565b600481565b602081565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160005414610f4f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f46906131b3565b60405180910390fd5b6002600081905550610f616000612152565b6001600081905550565b7f000000000000000000000000000000000000000000000000000000000000000081565b610f99828261240b565b5050565b6000610fa98383612421565b905092915050565b610fb9612439565b565b60606040518060400160405280600581526020017f312e302e30000000000000000000000000000000000000000000000000000000815250905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60035481565b600181565b600281565b600160005414611077576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161106e906131b3565b60405180910390fd5b60026000819055506000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146110e6576110e2612475565b8290505b60008083146110f55782611158565b60056000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020545b905060056000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548111156111f5576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008282829054906101000a90046fffffffffffffffffffffffffffffffff160392506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508060056000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546112c091906131d3565b92505081905550808273ffffffffffffffffffffffffffffffffffffffff167fc36b5179cb9c303b200074996eab2b3473eac370fdd7eba3bec636fe3510969660405160405180910390a3611334818373ffffffffffffffffffffffffffffffffffffffff1661201e90919063ffffffff16565b505060016000819055505050565b61134a612492565b565b601081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600061137f6124a6565b905090565b600161138f81612061565b6001600054146113d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113cb906131b3565b60405180910390fd5b600260008190555084849050878790501461141b576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61143334888890508561142e9190613207565b6124b4565b600061145f7f0000000000000000000000000000000000000000000000000000000000000000856124f2565b90506000805b898990508110156116c9576114e08a8a8381811061148657611485613249565b5b90506020028101906114989190613287565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506125e7565b60008a8a838181106114f5576114f4613249565b5b90506020028101906115079190613287565b8a8a8581811061151a57611519613249565b5b905060200201602081019061152f9190613316565b604051602001611541939291906133a9565b604051602081830303815290604052905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16858360405161159a919061340f565b60006040518083038185875af1925050503d80600081146115d7576040519150601f19603f3d011682016040523d82523d6000602084013e6115dc565b606091505b5050905080611617576040517fd3d2fd8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8484611623919061300c565b9350848a8a8581811061163957611638613249565b5b905060200201602081019061164e9190613316565b67ffffffffffffffff167f19cd6ffa1afc5a20b44dd0fea708b8fb38af1d7891d4929811a3c65ff1624fb98e8e8781811061168c5761168b613249565b5b905060200281019061169e9190613287565b6040516116ac929190613426565b60405180910390a3505080806116c19061344a565b915050611465565b506116d5348286612625565b5050600160008190555050505050505050565b600881565b6116f5612475565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361175b576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167feee59a71c694e68368a1cb0d135c448051bbfb12289e6c2223b0ceb100c2321d60405160405180910390a26117e881611f54565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fa1c3c7ba08cdab9542dfbb1f9606093e828bca6c2678c82942703ec4e223790260405160405180910390a35050565b600461185181612061565b6003548203156118a45760006003549050826003819055507f09aced8c098d4a0a1f486296935d5746bc64dfd41ed58ad84f4e5afab784fcf8838260405161189a9291906130fc565b60405180910390a1505b5050565b60026118b381612061565b6001600054146118f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118ef906131b3565b60405180910390fd5b6002600081905550600061192c7f0000000000000000000000000000000000000000000000000000000000000000856124f2565b90506000805b87879050811015611a4357600088888381811061195257611951613249565b5b90506020028101906119649190613492565b806000019061197391906134ba565b905014806119b65750603f88888381811061199157611990613249565b5b90506020028101906119a39190613492565b80600001906119b291906134ba565b9050115b156119ed576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878782818110611a00576119ff613249565b5b9050602002810190611a129190613492565b8060000190611a2191906134ba565b905082611a2e919061300c565b91508080611a3b9061344a565b915050611932565b5060008183611a529190613207565b9050611a5e34826124b4565b60005b88889050811015611e3157611aeb898983818110611a8257611a81613249565b5b9050602002810190611a949190613492565b8060200190611aa39190613287565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506125e7565b60005b898983818110611b0157611b00613249565b5b9050602002810190611b139190613492565b8060000190611b2291906134ba565b9050811015611e1d57611bcd8a8a84818110611b4157611b40613249565b5b9050602002810190611b539190613492565b8060000190611b6291906134ba565b83818110611b7357611b72613249565b5b9050602002810190611b859190613287565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506125e7565b60008a8a84818110611be257611be1613249565b5b9050602002810190611bf49190613492565b8060000190611c0391906134ba565b83818110611c1457611c13613249565b5b9050602002810190611c269190613287565b8c8c86818110611c3957611c38613249565b5b9050602002810190611c4b9190613492565b8060200190611c5a9190613287565b604051602001611c6d949392919061351d565b604051602081830303815290604052905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168783604051611cc6919061340f565b60006040518083038185875af1925050503d8060008114611d03576040519150601f19603f3d011682016040523d82523d6000602084013e611d08565b606091505b5050905080611d43576040517f1684b73e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b867f70327b6e1280f8015a284fca27cbbef10ac24c017f6452ff98212167813459d88d8d87818110611d7857611d77613249565b5b9050602002810190611d8a9190613492565b8060000190611d9991906134ba565b86818110611daa57611da9613249565b5b9050602002810190611dbc9190613287565b8f8f89818110611dcf57611dce613249565b5b9050602002810190611de19190613492565b8060200190611df09190613287565b604051611e009493929190613545565b60405180910390a250508080611e159061344a565b915050611aee565b508080611e299061344a565b915050611a61565b50611e3d348287612625565b50505060016000819055505050505050565b6010611e5a81612061565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611ec0576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167fab0699d690a6fcb85f70e4d28ee4c6114c3d90c12c579ca5abe089b8ff6ff1bf60405160405180910390a25050565b611f5181612700565b50565b611f5d81612741565b50565b600160005414611fa5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f9c906131b3565b60405180910390fd5b60026000819055506040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fdf906135cc565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000081565b60006120178261276b565b9050919050565b60005a60005a84865af161203a5763b12d13eb6000526004601cfd5b5050565b6120483382612786565b50565b612053612475565b61205d8282612796565b5050565b638b78c6d81954331461209557638b78c6d8600c5233600052806020600c205416612094576382b429006000526004601cfd5b5b50565b600081826120a585610ec8565b1614905092915050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d1560016000511417166120f4576390b8ec186000526004601cfd5b6000603452505050565b60006121086127a6565b67ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b60004790506000600460009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1690506000818361219a91906131d3565b90506000633b9aca007f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff166121d89190613207565b90506000808284101580156121ef57506000600354115b156122185760035484111561220f57600354915060035484039050612213565b8391505b61221c565b8390505b6fffffffffffffffffffffffffffffffff8016841115612268576040517f502cbcb200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008211156122e557816fffffffffffffffffffffffffffffffff166003600082825461229591906131d3565b925050819055507f09aced8c098d4a0a1f486296935d5746bc64dfd41ed58ad84f4e5afab784fcf8600354836003546122ce919061300c565b6040516122dc9291906130fc565b60405180910390a15b600187036123525760008211806122fc5750600081115b156123515780828661230e919061300c565b612318919061300c565b600460006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b5b61237f600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683896127b1565b6123ac600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682896127b1565b7f8b367b31582d5c638694615c1cfc77ffcf9febcf6e6c793358089f7769cfeee68282896040516123df939291906135ec565b60405180910390a150505050505050565b6000638b78c6d8600c52816000526020600c20549050919050565b612413612475565b61241d8282612786565b5050565b6000808261242e85610ec8565b161415905092915050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b638b78c6d819543314612490576382b429006000526004601cfd5b565b61249a612475565b6124a4600061288c565b565b6000638b78c6d81954905090565b808210156124ee576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60008060008473ffffffffffffffffffffffffffffffffffffffff1660405161251a90613649565b600060405180830381855afa9150503d8060008114612555576040519150601f19603f3d011682016040523d82523d6000602084013e61255a565b606091505b509150915081612596576040517f448e5c4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806125a090613690565b60001c9250838311156125df576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b6030815114612622576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b818311156126fb5760008173ffffffffffffffffffffffffffffffffffffffff16838561265291906131d3565b60405161265e90613649565b60006040518083038185875af1925050503d806000811461269b576040519150601f19603f3d011682016040523d82523d6000602084013e6126a0565b606091505b50509050806126f95782846126b591906131d3565b8273ffffffffffffffffffffffffffffffffffffffff167fff295b6ae91fb82f39d5bb544f1e62ce33f6978255b7c7434f8137bdf107f6d960405160405180910390a35b505b505050565b612708612475565b63389a75e1600c52806000526020600c20805442111561273057636f5e88186000526004601cfd5b600081555061273e8161288c565b50565b612749612475565b8060601b61275f57637448fbae6000526004601cfd5b6127688161288c565b50565b600063389a75e1600c52816000526020600c20549050919050565b612792828260006128ca565b5050565b6127a2828260016128ca565b5050565b60006202a300905090565b6000821115612887576001810361281d5781600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612811919061300c565b92505081905550612886565b600081036128535761284e828473ffffffffffffffffffffffffffffffffffffffff1661201e90919063ffffffff16565b612885565b6040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5b505050565b638b78c6d8198160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a38181555050565b638b78c6d8600c52826000526020600c208054838117836128ed57848216821890505b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3505050505050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006129598261292e565b9050919050565b6129698161294e565b811461297457600080fd5b50565b60008135905061298681612960565b92915050565b6000602082840312156129a2576129a1612924565b5b60006129b084828501612977565b91505092915050565b6000819050919050565b6129cc816129b9565b82525050565b60006020820190506129e760008301846129c3565b92915050565b60006fffffffffffffffffffffffffffffffff82169050919050565b612a12816129ed565b82525050565b6000602082019050612a2d6000830184612a09565b92915050565b612a3c8161294e565b82525050565b6000602082019050612a576000830184612a33565b92915050565b612a66816129b9565b8114612a7157600080fd5b50565b600081359050612a8381612a5d565b92915050565b600060208284031215612a9f57612a9e612924565b5b6000612aad84828501612a74565b91505092915050565b60008060408385031215612acd57612acc612924565b5b6000612adb85828601612977565b9250506020612aec85828601612a74565b9150509250929050565b60008115159050919050565b612b0b81612af6565b82525050565b6000602082019050612b266000830184612b02565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112612b5157612b50612b2c565b5b8235905067ffffffffffffffff811115612b6e57612b6d612b31565b5b602083019150836001820283011115612b8a57612b89612b36565b5b9250929050565b6000819050919050565b612ba481612b91565b8114612baf57600080fd5b50565b600081359050612bc181612b9b565b92915050565b60008060008060008060006080888a031215612be657612be5612924565b5b600088013567ffffffffffffffff811115612c0457612c03612929565b5b612c108a828b01612b3b565b9750975050602088013567ffffffffffffffff811115612c3357612c32612929565b5b612c3f8a828b01612b3b565b9550955050604088013567ffffffffffffffff811115612c6257612c61612929565b5b612c6e8a828b01612b3b565b93509350506060612c818a828b01612bb2565b91505092959891949750929550565b60008060408385031215612ca757612ca6612924565b5b6000612cb585828601612977565b9250506020612cc685828601612977565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b60005b83811015612d0a578082015181840152602081019050612cef565b60008484015250505050565b6000601f19601f8301169050919050565b6000612d3282612cd0565b612d3c8185612cdb565b9350612d4c818560208601612cec565b612d5581612d16565b840191505092915050565b60006020820190508181036000830152612d7a8184612d27565b905092915050565b600067ffffffffffffffff82169050919050565b612d9f81612d82565b82525050565b6000602082019050612dba6000830184612d96565b92915050565b60008083601f840112612dd657612dd5612b2c565b5b8235905067ffffffffffffffff811115612df357612df2612b31565b5b602083019150836020820283011115612e0f57612e0e612b36565b5b9250929050565b60008083601f840112612e2c57612e2b612b2c565b5b8235905067ffffffffffffffff811115612e4957612e48612b31565b5b602083019150836020820283011115612e6557612e64612b36565b5b9250929050565b60008060008060008060808789031215612e8957612e88612924565b5b600087013567ffffffffffffffff811115612ea757612ea6612929565b5b612eb389828a01612dc0565b9650965050602087013567ffffffffffffffff811115612ed657612ed5612929565b5b612ee289828a01612e16565b94509450506040612ef589828a01612a74565b9250506060612f0689828a01612977565b9150509295509295509295565b60008083601f840112612f2957612f28612b2c565b5b8235905067ffffffffffffffff811115612f4657612f45612b31565b5b602083019150836020820283011115612f6257612f61612b36565b5b9250929050565b60008060008060608587031215612f8357612f82612924565b5b600085013567ffffffffffffffff811115612fa157612fa0612929565b5b612fad87828801612f13565b94509450506020612fc087828801612a74565b9250506040612fd187828801612977565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000613017826129b9565b9150613022836129b9565b925082820190508082111561303a57613039612fdd565b5b92915050565b600082825260208201905092915050565b82818337600083830152505050565b600061306c8385613040565b9350613079838584613051565b61308283612d16565b840190509392505050565b61309681612b91565b82525050565b600060808201905081810360008301526130b781898b613060565b905081810360208301526130cc818789613060565b905081810360408301526130e1818587613060565b90506130f0606083018461308d565b98975050505050505050565b600060408201905061311160008301856129c3565b61311e60208301846129c3565b9392505050565b60008151905061313481612a5d565b92915050565b6000602082840312156131505761314f612924565b5b600061315e84828501613125565b91505092915050565b7f5245454e5452414e435900000000000000000000000000000000000000000000600082015250565b600061319d600a83612cdb565b91506131a882613167565b602082019050919050565b600060208201905081810360008301526131cc81613190565b9050919050565b60006131de826129b9565b91506131e9836129b9565b925082820390508181111561320157613200612fdd565b5b92915050565b6000613212826129b9565b915061321d836129b9565b925082820261322b816129b9565b9150828204841483151761324257613241612fdd565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126132a4576132a3613278565b5b80840192508235915067ffffffffffffffff8211156132c6576132c561327d565b5b6020830192506001820236038313156132e2576132e1613282565b5b509250929050565b6132f381612d82565b81146132fe57600080fd5b50565b600081359050613310816132ea565b92915050565b60006020828403121561332c5761332b612924565b5b600061333a84828501613301565b91505092915050565b600081905092915050565b600061335a8385613343565b9350613367838584613051565b82840190509392505050565b60008160c01b9050919050565b600061338b82613373565b9050919050565b6133a361339e82612d82565b613380565b82525050565b60006133b682858761334e565b91506133c28284613392565b600882019150819050949350505050565b600081519050919050565b60006133e9826133d3565b6133f38185613343565b9350613403818560208601612cec565b80840191505092915050565b600061341b82846133de565b915081905092915050565b60006020820190508181036000830152613441818486613060565b90509392505050565b6000613455826129b9565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361348757613486612fdd565b5b600182019050919050565b6000823560016040038336030381126134ae576134ad613278565b5b80830191505092915050565b600080833560016020038436030381126134d7576134d6613278565b5b80840192508235915067ffffffffffffffff8211156134f9576134f861327d565b5b60208301925060208202360383131561351557613514613282565b5b509250929050565b600061352a82868861334e565b915061353782848661334e565b915081905095945050505050565b60006040820190508181036000830152613560818688613060565b90508181036020830152613575818486613060565b905095945050505050565b7f4e6f7420696d706c656d656e7465640000000000000000000000000000000000600082015250565b60006135b6600f83612cdb565b91506135c182613580565b602082019050919050565b600060208201905081810360008301526135e5816135a9565b9050919050565b600060608201905061360160008301866129c3565b61360e60208301856129c3565b61361b60408301846129c3565b949350505050565b50565b6000613633600083613343565b915061363e82613623565b600082019050919050565b600061365482613626565b9150819050919050565b6000819050602082019050919050565b600061367a8251612b91565b80915050919050565b600082821b905092915050565b600061369b826133d3565b826136a58461365e565b90506136b08161366e565b925060208210156136f0576136eb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802613683565b831692505b505091905056fea26469706673582212205b83464c8159b4a300316c2a0b8a5da2e7cc180d9e2495b4c3be8d1a402389a864736f6c63430008130033a2646970667358221220aea92119bec81712bf6fc14aab2158d1db00f01c70decc4c8ae0f54e5391b91364736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000bbddc7ce488642fb579f8b00f3a59000725100000000000000000000000000000961ef480eb55e80d19ad83579a64c00700200000000000000000000000000000000219ab540356cbb839cbe05303d7705fa00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4f564d342e466163746f72790000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _consolidationSystemContract (address): 0x0000BBdDc7CE488642fb579F8B00f3a590007251
Arg [1] : _withdrawalSystemContract (address): 0x00000961Ef480Eb55e80D19ad83579A64c007002
Arg [2] : _depositSystemContract (address): 0x00000000219ab540356cBB839Cbe05303d7705Fa
Arg [3] : _ensName (string): OVM4.Factory
Arg [4] : _ensReverseRegistrar (address): 0x0000000000000000000000000000000000000000
Arg [5] : _ensOwner (address): 0x0000000000000000000000000000000000000000
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000bbddc7ce488642fb579f8b00f3a590007251
Arg [1] : 00000000000000000000000000000961ef480eb55e80d19ad83579a64c007002
Arg [2] : 00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 000000000000000000000000000000000000000000000000000000000000000c
Arg [7] : 4f564d342e466163746f72790000000000000000000000000000000000000000
Deployed Bytecode Sourcemap
101925:4454:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;103451:49;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;103505:46;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;105537:839;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;103394:52;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;103451:49;;;:::o;103505:46::-;;;:::o;105537:839::-;105704:24;105758:1;105741:19;;:5;:19;;;105737:47;;105769:15;;;;;;;;;;;;;;105737:47;105818:1;105795:25;;:11;:25;;;:58;;;;105851:1;105824:29;;:15;:29;;;105795:58;105791:92;;;105862:21;;;;;;;;;;;;;;105791:92;105916:1;105894:18;:23;;;105890:60;;105926:24;;;;;;;;;;;;;;105890:60;105982:10;105961:18;:31;;;105957:72;;;106001:28;;;;;;;;;;;;;;105957:72;106077:27;106113:24;106146:21;106176:5;106190:11;106210:15;106234:18;106044:215;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;106038:221;;106314:5;106273:97;;106308:3;106273:97;;;106321:11;106334:15;106351:18;106273:97;;;;;;;;:::i;:::-;;;;;;;;105537:839;;;;;;:::o;103394: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://aea92119bec81712bf6fc14aab2158d1db00f01c70decc4c8ae0f54e5391b913
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.