Overview
ETH Balance
0.742899331014344972 ETH
Eth Value
$2,492.41 (@ $3,354.98/ETH)More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 368 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Transfer | 21506869 | 6 hrs ago | IN | 0.01861787 ETH | 0.00007612 | ||||
Transfer | 21502268 | 21 hrs ago | IN | 0.20536691 ETH | 0.00015513 | ||||
Transfer | 21488551 | 2 days ago | IN | 0.03767403 ETH | 0.00018794 | ||||
Transfer | 21487378 | 2 days ago | IN | 0.0256856 ETH | 0.00024055 | ||||
Transfer | 21480329 | 3 days ago | IN | 0.01890788 ETH | 0.00056956 | ||||
Transfer | 21478655 | 4 days ago | IN | 0.01678419 ETH | 0.00010767 | ||||
Transfer | 21475195 | 4 days ago | IN | 0.02583145 ETH | 0.0001085 | ||||
Transfer | 21473857 | 4 days ago | IN | 0.02309675 ETH | 0.00018894 | ||||
Transfer | 21470642 | 5 days ago | IN | 0.02249128 ETH | 0.00013461 | ||||
Transfer | 21457187 | 7 days ago | IN | 0.13623932 ETH | 0.00011651 | ||||
Transfer | 21456620 | 7 days ago | IN | 0.01539668 ETH | 0.00012549 | ||||
Transfer | 21455662 | 7 days ago | IN | 0.01672189 ETH | 0.00012564 | ||||
Transfer | 21450674 | 8 days ago | IN | 0.0569289 ETH | 0.00017666 | ||||
Transfer | 21448906 | 8 days ago | IN | 0.03191495 ETH | 0.00019596 | ||||
Transfer | 21432982 | 10 days ago | IN | 0.03231374 ETH | 0.00033162 | ||||
Transfer | 21425676 | 11 days ago | IN | 0.01553969 ETH | 0.0002829 | ||||
Transfer | 21415993 | 12 days ago | IN | 0.04338814 ETH | 0.00056688 | ||||
Withdraw | 21413400 | 13 days ago | IN | 0 ETH | 0.00067652 | ||||
Transfer | 21411360 | 13 days ago | IN | 0.08095313 ETH | 0.00035551 | ||||
Transfer | 21411213 | 13 days ago | IN | 0.04557117 ETH | 0.00037912 | ||||
Transfer | 21408801 | 13 days ago | IN | 0.03140303 ETH | 0.0002336 | ||||
Transfer | 21408234 | 14 days ago | IN | 0.02748084 ETH | 0.00025242 | ||||
Transfer | 21405345 | 14 days ago | IN | 0.01785527 ETH | 0.00021912 | ||||
Transfer | 21404747 | 14 days ago | IN | 0.03092339 ETH | 0.00019602 | ||||
Transfer | 21400448 | 15 days ago | IN | 0.02843489 ETH | 0.00019071 |
Latest 22 internal transactions
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
21413400 | 13 days ago | 2.26485341 ETH | ||||
21413400 | 13 days ago | 0.87298173 ETH | ||||
21305707 | 28 days ago | 1.1245403 ETH | ||||
21305707 | 28 days ago | 0.71875733 ETH | ||||
21195612 | 43 days ago | 1.1823625 ETH | ||||
21195612 | 43 days ago | 0.89593689 ETH | ||||
21085280 | 59 days ago | 0.62535337 ETH | ||||
21085280 | 59 days ago | 0.62535337 ETH | ||||
20970236 | 75 days ago | 1.1075666 ETH | ||||
20970236 | 75 days ago | 1.1075666 ETH | ||||
20965938 | 75 days ago | 0.00017146 ETH | ||||
20910879 | 83 days ago | 0.00000457 ETH | ||||
20864193 | 89 days ago | 2.58371158 ETH | ||||
20864193 | 89 days ago | 2.58371158 ETH | ||||
20762139 | 104 days ago | 1.03921118 ETH | ||||
20762139 | 104 days ago | 1.03921118 ETH | ||||
20751577 | 105 days ago | 0 ETH | ||||
20655543 | 119 days ago | 1.37016214 ETH | ||||
20655543 | 119 days ago | 1.37016214 ETH | ||||
20536414 | 135 days ago | 0.84924742 ETH | ||||
20536414 | 135 days ago | 0.34099467 ETH | ||||
20475603 | 144 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0x78ce038ac24238c59777f5e4023707a23bb72d22
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x7109DeEb...e6C05163f The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
OracleFeeDistributor
Compiler Version
v0.8.10+commit.fc410830
Optimization Enabled:
No with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "../@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "../feeDistributorFactory/IFeeDistributorFactory.sol"; import "../assetRecovering/OwnableTokenRecoverer.sol"; import "./IFeeDistributor.sol"; import "../oracle/IOracle.sol"; import "../structs/P2pStructs.sol"; import "./BaseFeeDistributor.sol"; /// @notice Should be a Oracle contract /// @param _passedAddress passed address that does not support IOracle interface error OracleFeeDistributor__NotOracle(address _passedAddress); /// @notice cannot withdraw until rewards (CL+EL) are enough to be split error OracleFeeDistributor__WaitForEnoughRewardsToWithdraw(); /// @notice clientOnlyClRewards can only be set once error OracleFeeDistributor__CannotResetClientOnlyClRewards(); /// @notice Client basis points should be higher than 5000 error OracleFeeDistributor__ClientBasisPointsShouldBeHigherThan5000(); /// @title FeeDistributor accepting EL rewards only but splitting them with consideration of CL rewards /// @dev CL rewards are received by the client directly since client's address is ETH2 withdrawal credentials contract OracleFeeDistributor is BaseFeeDistributor { /// @notice Emits when clientOnlyClRewards has been updated /// @param _clientOnlyClRewards new value of clientOnlyClRewards event OracleFeeDistributor__ClientOnlyClRewardsUpdated( uint256 _clientOnlyClRewards ); /// @notice address of Oracle IOracle private immutable i_oracle; /// @notice amount of CL rewards (in Wei) that should belong to the client only /// and should not be considered for splitting between the service and the referrer uint256 s_clientOnlyClRewards; /// @dev Set values that are constant, common for all the clients, known at the initial deploy time. /// @param _oracle address of Oracle /// @param _factory address of FeeDistributorFactory /// @param _service address of the service (P2P) fee recipient constructor( address _oracle, address _factory, address payable _service ) BaseFeeDistributor(_factory, _service) { if (!ERC165Checker.supportsInterface(_oracle, type(IOracle).interfaceId)) { revert OracleFeeDistributor__NotOracle(_oracle); } i_oracle = IOracle(_oracle); } /// @inheritdoc IFeeDistributor function initialize( FeeRecipient calldata _clientConfig, FeeRecipient calldata _referrerConfig ) public override { if (_clientConfig.basisPoints <= 5000) { revert OracleFeeDistributor__ClientBasisPointsShouldBeHigherThan5000(); } super.initialize(_clientConfig, _referrerConfig); } /// @notice Set clientOnlyClRewards to a new value /// @param _clientOnlyClRewards new value of clientOnlyClRewards /// @dev may be needed when attaching this FeeDistributor to an existing validator. /// If previously earned rewards need not be split, they should be declared as client only. function setClientOnlyClRewards(uint256 _clientOnlyClRewards) external { i_factory.checkOperatorOrOwner(msg.sender); if (s_clientOnlyClRewards != 0) { revert OracleFeeDistributor__CannotResetClientOnlyClRewards(); } s_clientOnlyClRewards = _clientOnlyClRewards; emit OracleFeeDistributor__ClientOnlyClRewardsUpdated(_clientOnlyClRewards); } /// @notice Withdraw the whole balance of the contract according to the pre-defined basis points. /// @dev In case someone (either service, or client, or referrer) fails to accept ether, /// the owner will be able to recover some of their share. /// This scenario is very unlikely. It can only happen if that someone is a contract /// whose receive function changed its behavior since FeeDistributor's initialization. /// It can never happen unless the receiving party themselves wants it to happen. /// We strongly recommend against intentional reverts in the receive function /// because the remaining parties might call `withdraw` again multiple times without waiting /// for the owner to recover ether for the reverting party. /// In fact, as a punishment for the reverting party, before the recovering, /// 1 more regular `withdraw` will happen, rewarding the non-reverting parties again. /// `recoverEther` function is just an emergency backup plan and does not replace `withdraw`. /// /// @param _proof Merkle proof (the leaf's sibling, and each non-leaf hash that could not otherwise be calculated without additional leaf nodes) /// @param _amountInGwei total CL rewards earned by all validators in GWei (see _validatorCount) function withdraw( bytes32[] calldata _proof, uint256 _amountInGwei ) external nonReentrant { if (s_clientConfig.recipient == address(0)) { revert FeeDistributor__ClientNotSet(); } // get the contract's balance uint256 balance = address(this).balance; if (balance == 0) { // revert if there is no ether to withdraw revert FeeDistributor__NothingToWithdraw(); } // verify the data from the caller against the oracle i_oracle.verify(_proof, address(this), _amountInGwei); // Gwei to Wei uint256 amount = _amountInGwei * (10 ** 9); if (amount < s_clientOnlyClRewards) { // Can happen if the client has called emergencyEtherRecoveryWithoutOracleData before // but the actual rewards amount now appeared to be lower than the already split. // Should happen rarely. revert OracleFeeDistributor__WaitForEnoughRewardsToWithdraw(); } // total to split = EL + CL - already split part of CL (should be OK unless halfBalance < serviceAmount) uint256 totalAmountToSplit = balance + amount - s_clientOnlyClRewards; // set client basis points to value from storage config uint256 clientBp = s_clientConfig.basisPoints; // how much should service get uint256 serviceAmount = totalAmountToSplit - ((totalAmountToSplit * clientBp) / 10000); uint256 halfBalance = balance / 2; // how much should client get uint256 clientAmount; // if a half of the available balance is not enough to cover service (and referrer) shares // can happen when CL rewards (only accessible by client) are way much than EL rewards if (serviceAmount > halfBalance) { // client gets 50% of EL rewards clientAmount = halfBalance; // service (and referrer) get 50% of EL rewards combined (+1 wei in case balance is odd) serviceAmount = balance - halfBalance; // update the total amount being split to a smaller value to fit the actual balance of this contract totalAmountToSplit = (halfBalance * 10000) / (10000 - clientBp); } else { // send the remaining balance to client clientAmount = balance - serviceAmount; } emit OracleFeeDistributor__ClientOnlyClRewardsUpdated(s_clientOnlyClRewards); bool someEthSent; // how much should referrer get uint256 referrerAmount; if (s_referrerConfig.recipient != address(0)) { // if there is a referrer referrerAmount = (totalAmountToSplit * s_referrerConfig.basisPoints) / 10000; serviceAmount -= referrerAmount; // Send ETH to referrer. Ignore the possible yet unlikely revert in the receive function. someEthSent = P2pAddressLib._sendValue(s_referrerConfig.recipient, referrerAmount); } // Send ETH to service. Ignore the possible yet unlikely revert in the receive function. someEthSent = P2pAddressLib._sendValue(i_service, serviceAmount) || someEthSent; // Send ETH to client. Ignore the possible yet unlikely revert in the receive function. someEthSent = P2pAddressLib._sendValue(s_clientConfig.recipient, clientAmount) || someEthSent; if (someEthSent) { // client gets the rest from CL as not split anymore amount s_clientOnlyClRewards += (totalAmountToSplit - balance); } emit FeeDistributor__Withdrawn( serviceAmount, clientAmount, referrerAmount ); } /// @notice Recover ether in a rare case when either service, or client, or referrer /// refuse to accept ether. /// @param _to receiver address /// @param _proof Merkle proof (the leaf's sibling, and each non-leaf hash that could not otherwise be calculated without additional leaf nodes) /// @param _amountInGwei total CL rewards earned by all validators in GWei (see _validatorCount) function recoverEther( address payable _to, bytes32[] calldata _proof, uint256 _amountInGwei ) external onlyOwner { if (_to == address(0)) { revert FeeDistributor__ZeroAddressEthReceiver(); } this.withdraw(_proof, _amountInGwei); // get the contract's balance uint256 balance = address(this).balance; if (balance > 0) { // only happens if at least 1 party reverted in their receive bool success = P2pAddressLib._sendValueWithoutGasRestrictions(_to, balance); if (success) { emit FeeDistributor__EtherRecovered(_to, balance); } else { revert FeeDistributor__EtherRecoveryFailed(_to, balance); } } } /// @notice SHOULD NEVER BE CALLED NORMALLY!!!! Recover ether if oracle data (Merkle proof) is not available for some reason. function emergencyEtherRecoveryWithoutOracleData() external onlyClient nonReentrant { // get the contract's balance uint256 balance = address(this).balance; if (balance == 0) { // revert if there is no ether to withdraw revert FeeDistributor__NothingToWithdraw(); } uint256 halfBalance = balance / 2; // client gets 50% of EL rewards uint256 clientAmount = halfBalance; // service (and referrer) get 50% of EL rewards combined (+1 wei in case balance is odd) uint256 serviceAmount = balance - halfBalance; // the total amount being split fits the actual balance of this contract uint256 totalAmountToSplit = (halfBalance * 10000) / (10000 - s_clientConfig.basisPoints); emit OracleFeeDistributor__ClientOnlyClRewardsUpdated(s_clientOnlyClRewards); bool someEthSent; // how much should referrer get uint256 referrerAmount; if (s_referrerConfig.recipient != address(0)) { // if there is a referrer referrerAmount = (totalAmountToSplit * s_referrerConfig.basisPoints) / 10000; serviceAmount -= referrerAmount; // Send ETH to referrer. Ignore the possible yet unlikely revert in the receive function. someEthSent = P2pAddressLib._sendValue(s_referrerConfig.recipient, referrerAmount); } // Send ETH to service. Ignore the possible yet unlikely revert in the receive function. someEthSent = P2pAddressLib._sendValue(i_service, serviceAmount) || someEthSent; // Send ETH to client. Ignore the possible yet unlikely revert in the receive function. someEthSent = P2pAddressLib._sendValue(s_clientConfig.recipient, clientAmount) || someEthSent; if (someEthSent) { // client gets the rest from CL as not split anymore amount s_clientOnlyClRewards += (totalAmountToSplit - balance); } emit FeeDistributor__Withdrawn( serviceAmount, clientAmount, referrerAmount ); } /// @notice amount of CL rewards (in Wei) that should belong to the client only /// and should not be considered for splitting between the service and the referrer /// @return uint256 amount of client only CL rewards function clientOnlyClRewards() external view returns (uint256) { return s_clientOnlyClRewards; } /// @notice Returns the oracle address /// @return address oracle address function oracle() external view returns (address) { return address(i_oracle); } /// @inheritdoc Erc4337Account function withdrawSelector() public pure override returns (bytes4) { return OracleFeeDistributor.withdraw.selector; } /// @inheritdoc IFeeDistributor /// @dev client address function eth2WithdrawalCredentialsAddress() external override view returns (address) { return s_clientConfig.recipient; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity 0.8.10; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity 0.8.10; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.2) (utils/introspection/ERC165Checker.sol) pragma solidity 0.8.10; import "./IERC165.sol"; /** * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library ERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface, */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return _supportsERC165Interface(account, type(IERC165).interfaceId) && !_supportsERC165Interface(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && _supportsERC165Interface(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]); } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in _interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { if (!_supportsERC165Interface(account, interfaceIds[i])) { return false; } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * Interface identification is specified in ERC-165. */ function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) { // prepare call bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); // perform static call bool success; uint256 returnSize; uint256 returnValue; assembly { success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) returnSize := returndatasize() returnValue := mload(0x00) } return success && returnSize >= 0x20 && returnValue > 0; } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../@openzeppelin/contracts/utils/introspection/IERC165.sol"; import "../access/IOwnable.sol"; import "../feeDistributor/IFeeDistributor.sol"; import "../structs/P2pStructs.sol"; /// @dev External interface of FeeDistributorFactory declared to support ERC165 detection. interface IFeeDistributorFactory is IOwnable, IERC165 { /// @notice Emits when a new FeeDistributor instance has been created for a client /// @param _newFeeDistributorAddress address of the newly created FeeDistributor contract instance /// @param _clientAddress address of the client for whom the new instance was created /// @param _referenceFeeDistributor The address of the reference implementation of FeeDistributor used as the basis for clones /// @param _clientBasisPoints client basis points (percent * 100) event FeeDistributorFactory__FeeDistributorCreated( address indexed _newFeeDistributorAddress, address indexed _clientAddress, address indexed _referenceFeeDistributor, uint96 _clientBasisPoints ); /// @notice Emits when a new P2pEth2Depositor contract address has been set. /// @param _p2pEth2Depositor the address of the new P2pEth2Depositor contract event FeeDistributorFactory__P2pEth2DepositorSet( address indexed _p2pEth2Depositor ); /// @notice Emits when a new value of defaultClientBasisPoints has been set. /// @param _defaultClientBasisPoints new value of defaultClientBasisPoints event FeeDistributorFactory__DefaultClientBasisPointsSet( uint96 _defaultClientBasisPoints ); /// @notice Creates a FeeDistributor instance for a client /// @dev _referrerConfig can be zero if there is no referrer. /// /// @param _referenceFeeDistributor The address of the reference implementation of FeeDistributor used as the basis for clones /// @param _clientConfig address and basis points (percent * 100) of the client /// @param _referrerConfig address and basis points (percent * 100) of the referrer. /// @return newFeeDistributorAddress user FeeDistributor instance that has just been deployed function createFeeDistributor( address _referenceFeeDistributor, FeeRecipient calldata _clientConfig, FeeRecipient calldata _referrerConfig ) external returns (address newFeeDistributorAddress); /// @notice Computes the address of a FeeDistributor created by `createFeeDistributor` function /// @dev FeeDistributor instances are guaranteed to have the same address if all of /// 1) referenceFeeDistributor 2) clientConfig 3) referrerConfig /// are the same /// @param _referenceFeeDistributor The address of the reference implementation of FeeDistributor used as the basis for clones /// @param _clientConfig address and basis points (percent * 100) of the client /// @param _referrerConfig address and basis points (percent * 100) of the referrer. /// @return address user FeeDistributor instance that will be or has been deployed function predictFeeDistributorAddress( address _referenceFeeDistributor, FeeRecipient calldata _clientConfig, FeeRecipient calldata _referrerConfig ) external view returns (address); /// @notice Returns an array of client FeeDistributors /// @param _client client address /// @return address[] array of client FeeDistributors function allClientFeeDistributors( address _client ) external view returns (address[] memory); /// @notice Returns an array of all FeeDistributors for all clients /// @return address[] array of all FeeDistributors function allFeeDistributors() external view returns (address[] memory); /// @notice The address of P2pEth2Depositor /// @return address of P2pEth2Depositor function p2pEth2Depositor() external view returns (address); /// @notice Returns default client basis points /// @return default client basis points function defaultClientBasisPoints() external view returns (uint96); /// @notice Returns the current operator /// @return address of the current operator function operator() external view returns (address); /// @notice Reverts if the passed address is neither operator nor owner /// @param _address passed address function checkOperatorOrOwner(address _address) external view; /// @notice Reverts if the passed address is not P2pEth2Depositor /// @param _address passed address function checkP2pEth2Depositor(address _address) external view; /// @notice Reverts if the passed address is neither of: 1) operator 2) owner 3) P2pEth2Depositor /// @param _address passed address function check_Operator_Owner_P2pEth2Depositor(address _address) external view; }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, Lido <[email protected]> // SPDX-License-Identifier: MIT // https://github.com/lidofinance/lido-otc-seller/blob/master/contracts/lib/AssetRecoverer.sol pragma solidity 0.8.10; import "./TokenRecoverer.sol"; import "../access/OwnableBase.sol"; /// @title Token Recoverer with public functions callable by assetAccessingAddress /// @notice Recover ERC20, ERC721 and ERC1155 from a derived contract abstract contract OwnableTokenRecoverer is TokenRecoverer, OwnableBase { // Functions /** * @notice transfer an ERC20 token from this contract * @dev `SafeERC20.safeTransfer` doesn't always return a bool * as it performs an internal `require` check * @param _token address of the ERC20 token * @param _recipient address to transfer the tokens to * @param _amount amount of tokens to transfer */ function transferERC20( address _token, address _recipient, uint256 _amount ) external onlyOwner { _transferERC20(_token, _recipient, _amount); } /** * @notice transfer an ERC721 token from this contract * @dev `IERC721.safeTransferFrom` doesn't always return a bool * as it performs an internal `require` check * @param _token address of the ERC721 token * @param _recipient address to transfer the token to * @param _tokenId id of the individual token */ function transferERC721( address _token, address _recipient, uint256 _tokenId ) external onlyOwner { _transferERC721(_token, _recipient, _tokenId); } /** * @notice transfer an ERC1155 token from this contract * @dev see `AssetRecoverer` * @param _token address of the ERC1155 token that is being recovered * @param _recipient address to transfer the token to * @param _tokenId id of the individual token to transfer * @param _amount amount of tokens to transfer * @param _data data to transfer along */ function transferERC1155( address _token, address _recipient, uint256 _tokenId, uint256 _amount, bytes calldata _data ) external onlyOwner { _transferERC1155(_token, _recipient, _tokenId, _amount, _data); } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../@openzeppelin/contracts/utils/introspection/IERC165.sol"; import "../structs/P2pStructs.sol"; /// @dev External interface of FeeDistributor declared to support ERC165 detection. interface IFeeDistributor is IERC165 { /// @notice Emits once the client and the optional referrer have been set. /// @param _client address of the client. /// @param _clientBasisPoints basis points (percent * 100) of EL rewards that should go to the client /// @param _referrer address of the referrer. /// @param _referrerBasisPoints basis points (percent * 100) of EL rewards that should go to the referrer event FeeDistributor__Initialized( address indexed _client, uint96 _clientBasisPoints, address indexed _referrer, uint96 _referrerBasisPoints ); /// @notice Emits on successful withdrawal /// @param _serviceAmount how much wei service received /// @param _clientAmount how much wei client received /// @param _referrerAmount how much wei referrer received event FeeDistributor__Withdrawn( uint256 _serviceAmount, uint256 _clientAmount, uint256 _referrerAmount ); /// @notice Emits on request for a voluntary exit of validators /// @param _pubkeys pubkeys of validators event FeeDistributor__VoluntaryExit( bytes[] _pubkeys ); /// @notice Emits if case there was some ether left after `withdraw` and it has been sent successfully. /// @param _to destination address for ether. /// @param _amount how much wei the destination address received. event FeeDistributor__EtherRecovered( address indexed _to, uint256 _amount ); /// @notice Set client address. /// @dev Could not be in the constructor since it is different for different clients. /// _referrerConfig can be zero if there is no referrer. /// @param _clientConfig address and basis points (percent * 100) of the client /// @param _referrerConfig address and basis points (percent * 100) of the referrer. function initialize( FeeRecipient calldata _clientConfig, FeeRecipient calldata _referrerConfig ) external; /// @notice Increase the number of deposited validators. /// @dev Should be called when a new ETH2 deposit has been made /// @param _validatorCountToAdd number of newly deposited validators function increaseDepositedCount( uint32 _validatorCountToAdd ) external; /// @notice Request a voluntary exit of validators /// @dev Should be called by the client when they want to signal P2P that certain validators need to be exited /// @param _pubkeys pubkeys of validators function voluntaryExit( bytes[] calldata _pubkeys ) external; /// @notice Returns the factory address /// @return address factory address function factory() external view returns (address); /// @notice Returns the service address /// @return address service address function service() external view returns (address); /// @notice Returns the client address /// @return address client address function client() external view returns (address); /// @notice Returns the client basis points /// @return uint256 client basis points function clientBasisPoints() external view returns (uint256); /// @notice Returns the referrer address /// @return address referrer address function referrer() external view returns (address); /// @notice Returns the referrer basis points /// @return uint256 referrer basis points function referrerBasisPoints() external view returns (uint256); /// @notice Returns the address for ETH2 0x01 withdrawal credentials associated with this FeeDistributor /// @dev Return FeeDistributor's own address if FeeDistributor should be CL rewards recipient /// Otherwise, return the client address /// @return address address for ETH2 0x01 withdrawal credentials function eth2WithdrawalCredentialsAddress() external view returns (address); }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../@openzeppelin/contracts/utils/introspection/IERC165.sol"; import "../access/IOwnable.sol"; /** * @dev External interface of Oracle declared to support ERC165 detection. */ interface IOracle is IOwnable, IERC165 { // Events /** * @notice Emits when a new oracle report (Merkle root) recorded * @param _root Merkle root */ event Oracle__Reported(bytes32 indexed _root); // Functions /** * @notice Set a new oracle report (Merkle root) * @param _root Merkle root */ function report(bytes32 _root) external; /** * @notice Verify Merkle proof (that the leaf belongs to the tree) * @param _proof Merkle proof (the leaf's sibling, and each non-leaf hash that could not otherwise be calculated without additional leaf nodes) * @param _feeDistributorInstance feeDistributor instance address * @param _amountInGwei total CL rewards earned by all validators in GWei (see _validatorCount) */ function verify( bytes32[] calldata _proof, address _feeDistributorInstance, uint256 _amountInGwei ) external view; }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../feeDistributor/IFeeDistributor.sol"; /// @dev 256 bit struct /// @member basisPoints basis points (percent * 100) of EL rewards that should go to the recipient /// @member recipient address of the recipient struct FeeRecipient { uint96 basisPoints; address payable recipient; } /// @dev 256 bit struct /// @member depositedCount the number of deposited validators /// @member exitedCount the number of validators requested to exit /// @member collateralReturnedValue amount of ETH returned to the client to cover the collaterals /// @member cooldownUntil timestamp after which it will be possible to withdraw ignoring the client's revert on ETH receive struct ValidatorData { uint32 depositedCount; uint32 exitedCount; uint112 collateralReturnedValue; uint80 cooldownUntil; } /// @dev status of the client deposit /// @member None default status indicating that no ETH is waiting to be forwarded to Beacon DepositContract /// @member EthAdded client added ETH /// @member BeaconDepositInProgress P2P has forwarded some (but not all) ETH to Beacon DepositContract /// If all ETH has been forwarded, the status will be None. /// @member ServiceRejected P2P has rejected the service for a given FeeDistributor instance // The client can get a refund immediately. enum ClientDepositStatus { None, EthAdded, BeaconDepositInProgress, ServiceRejected } /// @dev 256 bit struct /// @member amount amount of ETH in wei to be used for an ETH2 deposit corresponding to a particular FeeDistributor instance /// @member expiration block timestamp after which the client will be able to get a refund /// @member status deposit status /// @member reservedForFutureUse unused space making up to 256 bit struct ClientDeposit { uint112 amount; uint40 expiration; ClientDepositStatus status; uint96 reservedForFutureUse; }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "../@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "../feeDistributorFactory/IFeeDistributorFactory.sol"; import "../assetRecovering/OwnableTokenRecoverer.sol"; import "../access/OwnableWithOperator.sol"; import "./IFeeDistributor.sol"; import "./FeeDistributorErrors.sol"; import "../structs/P2pStructs.sol"; import "../lib/P2pAddressLib.sol"; import "./Erc4337Account.sol"; /// @title Common logic for all FeeDistributor types abstract contract BaseFeeDistributor is Erc4337Account, OwnableTokenRecoverer, OwnableWithOperator, ReentrancyGuard, ERC165, IFeeDistributor { /// @notice FeeDistributorFactory address IFeeDistributorFactory internal immutable i_factory; /// @notice P2P fee recipient address address payable internal immutable i_service; /// @notice Client rewards recipient address and basis points FeeRecipient internal s_clientConfig; /// @notice Referrer rewards recipient address and basis points FeeRecipient internal s_referrerConfig; /// @notice If caller not client, revert modifier onlyClient() { address clientAddress = s_clientConfig.recipient; if (clientAddress != msg.sender) { revert FeeDistributor__CallerNotClient(msg.sender, clientAddress); } _; } /// @notice If caller not factory, revert modifier onlyFactory() { if (msg.sender != address(i_factory)) { revert FeeDistributor__NotFactoryCalled(msg.sender, i_factory); } _; } /// @dev Set values that are constant, common for all the clients, known at the initial deploy time. /// @param _factory address of FeeDistributorFactory /// @param _service address of the service (P2P) fee recipient constructor( address _factory, address payable _service ) { if (!ERC165Checker.supportsInterface(_factory, type(IFeeDistributorFactory).interfaceId)) { revert FeeDistributor__NotFactory(_factory); } if (_service == address(0)) { revert FeeDistributor__ZeroAddressService(); } i_factory = IFeeDistributorFactory(_factory); i_service = _service; bool serviceCanReceiveEther = P2pAddressLib._sendValue(_service, 0); if (!serviceCanReceiveEther) { revert FeeDistributor__ServiceCannotReceiveEther(_service); } } /// @inheritdoc IFeeDistributor function initialize( FeeRecipient calldata _clientConfig, FeeRecipient calldata _referrerConfig ) public virtual onlyFactory { if (_clientConfig.recipient == address(0)) { revert FeeDistributor__ZeroAddressClient(); } if (_clientConfig.recipient == i_service) { revert FeeDistributor__ClientAddressEqualsService(_clientConfig.recipient); } if (s_clientConfig.recipient != address(0)) { revert FeeDistributor__ClientAlreadySet(s_clientConfig.recipient); } if (_clientConfig.basisPoints >= 10000) { revert FeeDistributor__InvalidClientBasisPoints(_clientConfig.basisPoints); } if (_referrerConfig.recipient != address(0)) {// if there is a referrer if (_referrerConfig.recipient == i_service) { revert FeeDistributor__ReferrerAddressEqualsService(_referrerConfig.recipient); } if (_referrerConfig.recipient == _clientConfig.recipient) { revert FeeDistributor__ReferrerAddressEqualsClient(_referrerConfig.recipient); } if (_referrerConfig.basisPoints == 0) { revert FeeDistributor__ZeroReferrerBasisPointsForNonZeroReferrer(); } if (_clientConfig.basisPoints + _referrerConfig.basisPoints > 10000) { revert FeeDistributor__ClientPlusReferralBasisPointsExceed10000( _clientConfig.basisPoints, _referrerConfig.basisPoints ); } // set referrer config s_referrerConfig = _referrerConfig; } else {// if there is no referrer if (_referrerConfig.basisPoints != 0) { revert FeeDistributor__ReferrerBasisPointsMustBeZeroIfAddressIsZero(_referrerConfig.basisPoints); } } // set client config s_clientConfig = _clientConfig; emit FeeDistributor__Initialized( _clientConfig.recipient, _clientConfig.basisPoints, _referrerConfig.recipient, _referrerConfig.basisPoints ); bool clientCanReceiveEther = P2pAddressLib._sendValue(_clientConfig.recipient, 0); if (!clientCanReceiveEther) { revert FeeDistributor__ClientCannotReceiveEther(_clientConfig.recipient); } if (_referrerConfig.recipient != address(0)) {// if there is a referrer bool referrerCanReceiveEther = P2pAddressLib._sendValue(_referrerConfig.recipient, 0); if (!referrerCanReceiveEther) { revert FeeDistributor__ReferrerCannotReceiveEther(_referrerConfig.recipient); } } } /// @notice Accept ether from transactions receive() external payable { // only accept ether in an instance, not in a template if (s_clientConfig.recipient == address(0)) { revert FeeDistributor__ClientNotSet(); } } /// @inheritdoc IFeeDistributor function increaseDepositedCount(uint32 _validatorCountToAdd) external virtual { // Do nothing by default. Can be overridden. } /// @inheritdoc IFeeDistributor function voluntaryExit(bytes[] calldata _pubkeys) public virtual onlyClient { emit FeeDistributor__VoluntaryExit(_pubkeys); } /// @inheritdoc IFeeDistributor function factory() external view returns (address) { return address(i_factory); } /// @inheritdoc IFeeDistributor function service() external view returns (address) { return i_service; } /// @inheritdoc IFeeDistributor function client() public view override(Erc4337Account, IFeeDistributor) returns (address) { return s_clientConfig.recipient; } /// @inheritdoc IFeeDistributor function clientBasisPoints() external view returns (uint256) { return s_clientConfig.basisPoints; } /// @inheritdoc IFeeDistributor function referrer() external view returns (address) { return s_referrerConfig.recipient; } /// @inheritdoc IFeeDistributor function referrerBasisPoints() external view returns (uint256) { return s_referrerConfig.basisPoints; } /// @inheritdoc IFeeDistributor function eth2WithdrawalCredentialsAddress() external virtual view returns (address); /// @inheritdoc ERC165 function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IFeeDistributor).interfaceId || super.supportsInterface(interfaceId); } /// @inheritdoc IOwnable function owner() public view override(Erc4337Account, OwnableBase, Ownable) returns (address) { return i_factory.owner(); } /// @inheritdoc IOwnableWithOperator function operator() public view override(Erc4337Account, OwnableWithOperator) returns (address) { return super.operator(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity 0.8.10; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; /** * @dev External interface of Ownable. */ interface IOwnable { /** * @dev Returns the address of the current owner. */ function owner() external view returns (address); }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, Lido <[email protected]> // SPDX-License-Identifier: MIT // https://github.com/lidofinance/lido-otc-seller/blob/master/contracts/lib/AssetRecoverer.sol pragma solidity 0.8.10; import {IERC20} from "../@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721} from "../@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC1155} from "../@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import {SafeERC20} from "../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @notice prevents burn for transfer functions * @dev _recipient should not be a zero address */ error TokenRecoverer__NoBurn(); /// @title Token Recoverer /// @notice Recover ERC20, ERC721 and ERC1155 from a derived contract abstract contract TokenRecoverer { using SafeERC20 for IERC20; event ERC20Transferred(address indexed _token, address indexed _recipient, uint256 _amount); event ERC721Transferred(address indexed _token, address indexed _recipient, uint256 _tokenId); event ERC1155Transferred(address indexed _token, address indexed _recipient, uint256 _tokenId, uint256 _amount, bytes _data); /** * @notice prevents burn for transfer functions * @dev checks for zero address and reverts if true * @param _recipient address of the transfer recipient */ modifier burnDisallowed(address _recipient) { if (_recipient == address(0)) { revert TokenRecoverer__NoBurn(); } _; } /** * @notice transfer an ERC20 token from this contract * @dev `SafeERC20.safeTransfer` doesn't always return a bool * as it performs an internal `require` check * @param _token address of the ERC20 token * @param _recipient address to transfer the tokens to * @param _amount amount of tokens to transfer */ function _transferERC20( address _token, address _recipient, uint256 _amount ) internal virtual burnDisallowed(_recipient) { IERC20(_token).safeTransfer(_recipient, _amount); emit ERC20Transferred(_token, _recipient, _amount); } /** * @notice transfer an ERC721 token from this contract * @dev `IERC721.safeTransferFrom` doesn't always return a bool * as it performs an internal `require` check * @param _token address of the ERC721 token * @param _recipient address to transfer the token to * @param _tokenId id of the individual token */ function _transferERC721( address _token, address _recipient, uint256 _tokenId ) internal virtual burnDisallowed(_recipient) { IERC721(_token).transferFrom(address(this), _recipient, _tokenId); emit ERC721Transferred(_token, _recipient, _tokenId); } /** * @notice transfer an ERC1155 token from this contract * @dev `IERC1155.safeTransferFrom` doesn't always return a bool * as it performs an internal `require` check * @param _token address of the ERC1155 token that is being recovered * @param _recipient address to transfer the token to * @param _tokenId id of the individual token to transfer * @param _amount amount of tokens to transfer * @param _data data to transfer along */ function _transferERC1155( address _token, address _recipient, uint256 _tokenId, uint256 _amount, bytes calldata _data ) internal virtual burnDisallowed(_recipient) { IERC1155(_token).safeTransferFrom(address(this), _recipient, _tokenId, _amount, _data); emit ERC1155Transferred(_token, _recipient, _tokenId, _amount, _data); } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../@openzeppelin/contracts/utils/Context.sol"; import "./IOwnable.sol"; /** * @notice Throws if called by any account other than the owner. * @param _caller address of the caller * @param _owner address of the owner */ error OwnableBase__CallerNotOwner(address _caller, address _owner); /** * @dev minimalistic version of OpenZeppelin's Ownable. * The owner is abstract and is not persisted in storage. * Needs to be overridden in a child contract. */ abstract contract OwnableBase is Context, IOwnable { /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { address caller = _msgSender(); address currentOwner = owner(); if (currentOwner != caller) { revert OwnableBase__CallerNotOwner(caller, currentOwner); } _; } /** * @dev Returns the address of the current owner. * Needs to be overridden in a child contract. */ function owner() public view virtual override returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity 0.8.10; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol) pragma solidity 0.8.10; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol) pragma solidity 0.8.10; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. * * _Available since v3.1._ */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the amount of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity 0.8.10; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity 0.8.10; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity 0.8.10; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity 0.8.10; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "./Ownable2Step.sol"; import "./IOwnableWithOperator.sol"; /** * @notice newOperator is the zero address */ error Access__ZeroNewOperator(); /** * @notice newOperator is the same as the old one */ error Access__SameOperator(address _operator); /** * @notice caller is neither the operator nor owner */ error Access__CallerNeitherOperatorNorOwner(address _caller, address _operator, address _owner); /** * @notice address is neither the operator nor owner */ error Access__AddressNeitherOperatorNorOwner(address _address, address _operator, address _owner); /** * @dev Ownable with an additional role of operator */ abstract contract OwnableWithOperator is Ownable2Step, IOwnableWithOperator { address private s_operator; /** * @dev Emits when the operator has been changed * @param _previousOperator address of the previous operator * @param _newOperator address of the new operator */ event OperatorChanged( address indexed _previousOperator, address indexed _newOperator ); /** * @dev Throws if called by any account other than the operator or the owner. */ modifier onlyOperatorOrOwner() { address currentOwner = owner(); address currentOperator = s_operator; if (currentOperator != _msgSender() && currentOwner != _msgSender()) { revert Access__CallerNeitherOperatorNorOwner(_msgSender(), currentOperator, currentOwner); } _; } function checkOperatorOrOwner(address _address) public view virtual { address currentOwner = owner(); address currentOperator = s_operator; if (_address == address(0) || (currentOperator != _address && currentOwner != _address)) { revert Access__AddressNeitherOperatorNorOwner(_address, currentOperator, currentOwner); } } /** * @dev Returns the current operator. */ function operator() public view virtual returns (address) { return s_operator; } /** * @dev Transfers operator to a new account (`newOperator`). * Can only be called by the current owner. */ function changeOperator(address _newOperator) external virtual onlyOwner { if (_newOperator == address(0)) { revert Access__ZeroNewOperator(); } if (_newOperator == s_operator) { revert Access__SameOperator(_newOperator); } _changeOperator(_newOperator); } /** * @dev Transfers operator to a new account (`newOperator`). * Internal function without access restriction. */ function _changeOperator(address _newOperator) internal virtual { address oldOperator = s_operator; s_operator = _newOperator; emit OperatorChanged(oldOperator, _newOperator); } /** * @dev Dismisses the old operator without setting a new one. * Can only be called by the current owner. */ function dismissOperator() external virtual onlyOwner { _changeOperator(address(0)); } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../feeDistributorFactory/IFeeDistributorFactory.sol"; /// @notice Should be a FeeDistributorFactory contract /// @param _passedAddress passed address that does not support IFeeDistributorFactory interface error FeeDistributor__NotFactory(address _passedAddress); /// @notice Service address should be a secure P2P address, not zero. error FeeDistributor__ZeroAddressService(); /// @notice Client address should be different from service address. /// @param _passedAddress passed client address that equals to the service address error FeeDistributor__ClientAddressEqualsService(address _passedAddress); /// @notice Client address should be an actual client address, not zero. error FeeDistributor__ZeroAddressClient(); /// @notice Client basis points should be >= 0 and <= 10000 /// @param _clientBasisPoints passed incorrect client basis points error FeeDistributor__InvalidClientBasisPoints(uint96 _clientBasisPoints); /// @notice Referrer basis points should be > 0 if the referrer exists error FeeDistributor__ZeroReferrerBasisPointsForNonZeroReferrer(); /// @notice The sum of (Client basis points + Referral basis points) should be >= 0 and <= 10000 /// @param _clientBasisPoints passed client basis points /// @param _referralBasisPoints passed referral basis points error FeeDistributor__ClientPlusReferralBasisPointsExceed10000(uint96 _clientBasisPoints, uint96 _referralBasisPoints); /// @notice Referrer address should be different from service address. /// @param _passedAddress passed referrer address that equals to the service address error FeeDistributor__ReferrerAddressEqualsService(address _passedAddress); /// @notice Referrer address should be different from client address. /// @param _passedAddress passed referrer address that equals to the client address error FeeDistributor__ReferrerAddressEqualsClient(address _passedAddress); /// @notice Only factory can call `initialize`. /// @param _msgSender sender address. /// @param _actualFactory the actual factory address that can call `initialize`. error FeeDistributor__NotFactoryCalled(address _msgSender, IFeeDistributorFactory _actualFactory); /// @notice `initialize` should only be called once. /// @param _existingClient address of the client with which the contact has already been initialized. error FeeDistributor__ClientAlreadySet(address _existingClient); /// @notice Cannot call `withdraw` if the client address is not set yet. /// @dev The client address is supposed to be set by the factory. error FeeDistributor__ClientNotSet(); /// @notice basisPoints of the referrer must be zero if referrer address is empty. /// @param _referrerBasisPoints basisPoints of the referrer. error FeeDistributor__ReferrerBasisPointsMustBeZeroIfAddressIsZero(uint96 _referrerBasisPoints); /// @notice service should be able to receive ether. /// @param _service address of the service. error FeeDistributor__ServiceCannotReceiveEther(address _service); /// @notice client should be able to receive ether. /// @param _client address of the client. error FeeDistributor__ClientCannotReceiveEther(address _client); /// @notice referrer should be able to receive ether. /// @param _referrer address of the referrer. error FeeDistributor__ReferrerCannotReceiveEther(address _referrer); /// @notice zero ether balance error FeeDistributor__NothingToWithdraw(); /// @notice Throws if called by any account other than the client. /// @param _caller address of the caller /// @param _client address of the client error FeeDistributor__CallerNotClient(address _caller, address _client); /// @notice Throws in case there was some ether left after `withdraw` and it has failed to recover. /// @param _to destination address for ether. /// @param _amount how much wei the destination address should have received, but didn't. error FeeDistributor__EtherRecoveryFailed( address _to, uint256 _amount ); /// @notice ETH receiver should not be a zero address error FeeDistributor__ZeroAddressEthReceiver();
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; library P2pAddressLib { /// @notice Sends amount of ETH in wei to recipient /// @param _recipient address of recipient /// @param _amount amount of ETH in wei /// @return bool whether send succeeded function _sendValue(address payable _recipient, uint256 _amount) internal returns (bool) { (bool success, ) = _recipient.call{ value: _amount, gas: gasleft() / 4 // to prevent DOS, should be enough in normal cases }(""); return success; } /// @notice Sends amount of ETH in wei to recipient /// @param _recipient address of recipient /// @param _amount amount of ETH in wei /// @return bool whether send succeeded function _sendValueWithoutGasRestrictions(address payable _recipient, uint256 _amount) internal returns (bool) { (bool success, ) = _recipient.call{ value: _amount }(""); return success; } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "../@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "../erc4337/IAccount.sol"; import "../erc4337/IEntryPointStakeManager.sol"; import "../erc4337/UserOperation.sol"; import "../access/IOwnableWithOperator.sol"; /// @notice passed address should be a valid ERC-4337 entryPoint /// @param _passedAddress passed address error Erc4337Account__NotEntryPoint(address _passedAddress); /// @notice data length should be at least 4 byte to be a function signature error Erc4337Account__DataTooShort(); /// @notice only withdraw function is allowed to be called via ERC-4337 UserOperation error Erc4337Account__OnlyWithdrawIsAllowed(); /// @notice only client, owner, and operator are allowed to withdraw from EntryPoint error Erc4337Account__NotAllowedToWithdrawFromEntryPoint(); /// @title gasless withdraw for FeeDistributors via ERC-4337 abstract contract Erc4337Account is IAccount, IOwnableWithOperator { using ECDSA for bytes32; /// @notice withdraw without arguments bytes4 private constant defaultWithdrawSelector = bytes4(keccak256("withdraw()")); /// @notice Singleton ERC-4337 entryPoint 0.6.0 used by this account address payable constant entryPoint = payable(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789); /// @inheritdoc IAccount function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds ) external override returns (uint256 validationData) { if (msg.sender != entryPoint) { revert Erc4337Account__NotEntryPoint(msg.sender); } validationData = _validateSignature(userOp, userOpHash); bytes4 selector = _getFunctionSelector(userOp.callData); if (selector != withdrawSelector()) { revert Erc4337Account__OnlyWithdrawIsAllowed(); } _payPrefund(missingAccountFunds); } /// @notice Withdraw this contract's balance from EntryPoint back to this contract function withdrawFromEntryPoint() external { if (!( msg.sender == owner() || msg.sender == operator() || msg.sender == client() )) { revert Erc4337Account__NotAllowedToWithdrawFromEntryPoint(); } uint256 balance = IEntryPointStakeManager(entryPoint).balanceOf(address(this)); IEntryPointStakeManager(entryPoint).withdrawTo(payable(address(this)), balance); } /// @notice Validates the signature of a user operation. /// @param _userOp the operation that is about to be executed. /// @param _userOpHash hash of the user's request data. can be used as the basis for signature. /// @return validationData 0 for valid signature, 1 to mark signature failure function _validateSignature( UserOperation calldata _userOp, bytes32 _userOpHash ) private view returns (uint256 validationData) { bytes32 hash = _userOpHash.toEthSignedMessageHash(); address signer = hash.recover(_userOp.signature); if ( signer == operator() || signer == client() ) { validationData = 0; } else { validationData = 1; } } /// @notice Returns function selector (first 4 bytes of data) /// @param _data calldata (encoded signature + arguments) /// @return functionSelector function selector function _getFunctionSelector(bytes calldata _data) private pure returns (bytes4 functionSelector) { if (_data.length < 4) { revert Erc4337Account__DataTooShort(); } return bytes4(_data[:4]); } /// @notice sends to the entrypoint (msg.sender) the missing funds for this transaction. /// @param _missingAccountFunds the minimum value this method should send the entrypoint. /// this value MAY be zero, in case there is enough deposit, or the userOp has a paymaster. function _payPrefund(uint256 _missingAccountFunds) private { if (_missingAccountFunds != 0) { (bool success, ) = payable(msg.sender).call{ value: _missingAccountFunds, gas: type(uint256).max }(""); (success); //ignore failure (its EntryPoint's job to verify, not account.) } } /// @notice Returns the client address /// @return address client address function client() public view virtual returns (address); /// @inheritdoc IOwnable function owner() public view virtual returns (address); /// @inheritdoc IOwnableWithOperator function operator() public view virtual returns (address); /// @notice withdraw function selector /// @dev since withdraw function in derived contracts can have arguments, its /// signature can vary and can be overridden in derived contracts /// @return bytes4 withdraw function selector function withdrawSelector() public pure virtual returns (bytes4) { return defaultWithdrawSelector; } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "./Ownable.sol"; /** * @notice caller must be pendingOwner */ error Ownable2Step__CallerNotNewOwner(); /** * @notice new owner address should be different from the current owner */ error Ownable2Step__NewOwnerShouldNotBeCurrentOwner(); /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private s_pendingOwner; /** * @dev Emits in transferOwnership (start of the transfer) * @param _previousOwner address of the previous owner * @param _newOwner address of the new owner */ event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return s_pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { address currentOwner = owner(); if (newOwner == currentOwner) { revert Ownable2Step__NewOwnerShouldNotBeCurrentOwner(); } s_pendingOwner = newOwner; emit OwnershipTransferStarted(currentOwner, newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete s_pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() external { address sender = _msgSender(); if (pendingOwner() != sender) { revert Ownable2Step__CallerNotNewOwner(); } _transferOwnership(sender); } }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "./IOwnable.sol"; /** * @dev Ownable with an additional role of operator */ interface IOwnableWithOperator is IOwnable { /** * @dev Returns the current operator. */ function operator() external view returns (address); }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "./OwnableBase.sol"; /** * @notice _newOwner cannot be a zero address */ error Ownable__NewOwnerIsZeroAddress(); /** * @dev OpenZeppelin's Ownable with modifier onlyOwner extracted to OwnableBase * and removed `renounceOwnership` */ abstract contract Ownable is OwnableBase { /** * @dev Emits when the owner has been changed. * @param _previousOwner address of the previous owner * @param _newOwner address of the new owner */ event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner); address private s_owner; /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual override returns (address) { return s_owner; } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. * @param _newOwner address of the new owner */ function transferOwnership(address _newOwner) external virtual onlyOwner { if (_newOwner == address(0)) { revert Ownable__NewOwnerIsZeroAddress(); } _transferOwnership(_newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. * @param _newOwner address of the new owner */ function _transferOwnership(address _newOwner) internal virtual { address oldOwner = s_owner; s_owner = _newOwner; emit OwnershipTransferred(oldOwner, _newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/cryptography/ECDSA.sol) pragma solidity 0.8.10; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s; uint8 v; assembly { s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) v := add(shr(255, vs), 27) } return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } if (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.10; import "./UserOperation.sol"; interface IAccount { /** * Validate user's signature and nonce * the entryPoint will make the call to the recipient only if this validation call returns successfully. * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). * This allows making a "simulation call" without a valid signature * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. * * @dev Must validate caller is the entryPoint. * Must validate the signature and nonce * @param userOp the operation that is about to be executed. * @param userOpHash hash of the user's request data. can be used as the basis for signature. * @param missingAccountFunds missing funds on the account's deposit in the entrypoint. * This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call. * The excess is left as a deposit in the entrypoint, for future calls. * can be withdrawn anytime using "entryPoint.withdrawTo()" * In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero. * @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "authorizer" contract. * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" * <6-byte> validAfter - first timestamp this operation is valid * If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure. * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) external returns (uint256 validationData); }
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.10; interface IEntryPointStakeManager { /// @return the deposit (for gas payment) of the account function balanceOf(address account) external view returns (uint256); /** * withdraw from the deposit. * @param withdrawAddress the address to send withdrawn value. * @param withdrawAmount the amount to withdraw. */ function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.10; /** * User Operation struct * @param sender the sender account of this request. * @param nonce unique value the sender uses to verify it is not a replay. * @param initCode if set, the account contract will be created by this constructor/ * @param callData the method call to execute on this account. * @param callGasLimit the gas limit passed to the callData method call. * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp. * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead. * @param maxFeePerGas same as EIP-1559 gas parameter. * @param maxPriorityFeePerGas same as EIP-1559 gas parameter. * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender. * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID. */ struct UserOperation { address sender; uint256 nonce; bytes initCode; bytes callData; uint256 callGasLimit; uint256 verificationGasLimit; uint256 preVerificationGas; uint256 maxFeePerGas; uint256 maxPriorityFeePerGas; bytes paymasterAndData; bytes signature; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity 0.8.10; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
{ "optimizer": { "enabled": false, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address payable","name":"_service","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"Access__AddressNeitherOperatorNorOwner","type":"error"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"Access__SameOperator","type":"error"},{"inputs":[],"name":"Access__ZeroNewOperator","type":"error"},{"inputs":[],"name":"Erc4337Account__DataTooShort","type":"error"},{"inputs":[],"name":"Erc4337Account__NotAllowedToWithdrawFromEntryPoint","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"Erc4337Account__NotEntryPoint","type":"error"},{"inputs":[],"name":"Erc4337Account__OnlyWithdrawIsAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_client","type":"address"}],"name":"FeeDistributor__CallerNotClient","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__ClientAddressEqualsService","type":"error"},{"inputs":[{"internalType":"address","name":"_existingClient","type":"address"}],"name":"FeeDistributor__ClientAlreadySet","type":"error"},{"inputs":[{"internalType":"address","name":"_client","type":"address"}],"name":"FeeDistributor__ClientCannotReceiveEther","type":"error"},{"inputs":[],"name":"FeeDistributor__ClientNotSet","type":"error"},{"inputs":[{"internalType":"uint96","name":"_clientBasisPoints","type":"uint96"},{"internalType":"uint96","name":"_referralBasisPoints","type":"uint96"}],"name":"FeeDistributor__ClientPlusReferralBasisPointsExceed10000","type":"error"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"FeeDistributor__EtherRecoveryFailed","type":"error"},{"inputs":[{"internalType":"uint96","name":"_clientBasisPoints","type":"uint96"}],"name":"FeeDistributor__InvalidClientBasisPoints","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__NotFactory","type":"error"},{"inputs":[{"internalType":"address","name":"_msgSender","type":"address"},{"internalType":"contract IFeeDistributorFactory","name":"_actualFactory","type":"address"}],"name":"FeeDistributor__NotFactoryCalled","type":"error"},{"inputs":[],"name":"FeeDistributor__NothingToWithdraw","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__ReferrerAddressEqualsClient","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__ReferrerAddressEqualsService","type":"error"},{"inputs":[{"internalType":"uint96","name":"_referrerBasisPoints","type":"uint96"}],"name":"FeeDistributor__ReferrerBasisPointsMustBeZeroIfAddressIsZero","type":"error"},{"inputs":[{"internalType":"address","name":"_referrer","type":"address"}],"name":"FeeDistributor__ReferrerCannotReceiveEther","type":"error"},{"inputs":[{"internalType":"address","name":"_service","type":"address"}],"name":"FeeDistributor__ServiceCannotReceiveEther","type":"error"},{"inputs":[],"name":"FeeDistributor__ZeroAddressClient","type":"error"},{"inputs":[],"name":"FeeDistributor__ZeroAddressEthReceiver","type":"error"},{"inputs":[],"name":"FeeDistributor__ZeroAddressService","type":"error"},{"inputs":[],"name":"FeeDistributor__ZeroReferrerBasisPointsForNonZeroReferrer","type":"error"},{"inputs":[],"name":"OracleFeeDistributor__CannotResetClientOnlyClRewards","type":"error"},{"inputs":[],"name":"OracleFeeDistributor__ClientBasisPointsShouldBeHigherThan5000","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"OracleFeeDistributor__NotOracle","type":"error"},{"inputs":[],"name":"OracleFeeDistributor__WaitForEnoughRewardsToWithdraw","type":"error"},{"inputs":[],"name":"Ownable2Step__CallerNotNewOwner","type":"error"},{"inputs":[],"name":"Ownable2Step__NewOwnerShouldNotBeCurrentOwner","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"OwnableBase__CallerNotOwner","type":"error"},{"inputs":[],"name":"TokenRecoverer__NoBurn","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"_data","type":"bytes"}],"name":"ERC1155Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"ERC20Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ERC721Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"FeeDistributor__EtherRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_client","type":"address"},{"indexed":false,"internalType":"uint96","name":"_clientBasisPoints","type":"uint96"},{"indexed":true,"internalType":"address","name":"_referrer","type":"address"},{"indexed":false,"internalType":"uint96","name":"_referrerBasisPoints","type":"uint96"}],"name":"FeeDistributor__Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes[]","name":"_pubkeys","type":"bytes[]"}],"name":"FeeDistributor__VoluntaryExit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_serviceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_clientAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_referrerAmount","type":"uint256"}],"name":"FeeDistributor__Withdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOperator","type":"address"},{"indexed":true,"internalType":"address","name":"_newOperator","type":"address"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_clientOnlyClRewards","type":"uint256"}],"name":"OracleFeeDistributor__ClientOnlyClRewardsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOperator","type":"address"}],"name":"changeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"checkOperatorOrOwner","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"client","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clientBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clientOnlyClRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dismissOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyEtherRecoveryWithoutOracleData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eth2WithdrawalCredentialsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_validatorCountToAdd","type":"uint32"}],"name":"increaseDepositedCount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct FeeRecipient","name":"_clientConfig","type":"tuple"},{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct FeeRecipient","name":"_referrerConfig","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"uint256","name":"_amountInGwei","type":"uint256"}],"name":"recoverEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"referrer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referrerBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"service","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_clientOnlyClRewards","type":"uint256"}],"name":"setClientOnlyClRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferERC1155","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_pubkeys","type":"bytes[]"}],"name":"voluntaryExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"uint256","name":"_amountInGwei","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFromEntryPoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawSelector","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"stateMutability":"payable","type":"receive"}]
Latest 12 blocks produced
Block | Transaction | Difficulty | Gas Used | Reward | |
---|---|---|---|---|---|
21295942 | 29 days ago | 160 | 0.00 TH | 12,713,162 (42.38%) | 0.026084104401060024 ETH |
21103847 | 56 days ago | 92 | 0.00 TH | 7,063,615 (23.55%) | 0.006386322677838959 ETH |
21016421 | 68 days ago | 182 | 0.00 TH | 8,199,659 (27.33%) | 0.010408506535460572 ETH |
21014314 | 69 days ago | 173 | 0.00 TH | 13,621,524 (45.41%) | 0.014296981744211428 ETH |
20965938 | 75 days ago | 117 | 0.00 TH | 11,182,659 (37.28%) | 0.034520727180089775 ETH |
20910879 | 83 days ago | 116 | 0.00 TH | 11,388,450 (37.96%) | 0.030835521109040492 ETH |
20841931 | 93 days ago | 129 | 0.00 TH | 7,565,779 (25.22%) | 0.008395497988124246 ETH |
20785964 | 100 days ago | 150 | 0.00 TH | 13,650,486 (45.50%) | 0.076922946471276825 ETH |
20751577 | 105 days ago | 175 | 0.00 TH | 11,443,703 (38.15%) | 0.008623089801512633 ETH |
20740189 | 107 days ago | 83 | 0.00 TH | 6,029,420 (20.10%) | 0.006051856303477457 ETH |
20591634 | 128 days ago | 134 | 0.00 TH | 15,386,493 (51.29%) | 0.038278488054455806 ETH |
20504111 | 140 days ago | 122 | 0.00 TH | 7,644,342 (25.48%) | 0.019276442176379338 ETH |
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | Ether (ETH) | 100.00% | $3,354.98 | 0.7429 | $2,492.41 |
Loading...
Loading
[ Download: CSV Export ]
[ 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.