Source Code
Overview
S Balance
More Info
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
InfinexFaucet
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// c=< // | // | ////\ 1@2 // @@ | /___\** @@@2 @@@@@@@@@@@@@@@@@@@@@@ // @@@ | |~L~ |* @@@@@@ @@@ @@@@@ @@@@ @@@ @@@@ @@@ @@@@@@@@ @@@@ @@@@ @@@ @@@@@@@@@ @@@@ @@@@ // @@@@@ | \=_/8 @@@@1@@ @@@ @@@@@ @@@@ @@@@ @@@ @@@@@ @@@ @@@@@@@@@ @@@@ @@@@@ @@@@ @@@@@@@@@ @@@@ @@@@ // @@@@@@| _ /| |\__ @@@@@@@@2 @@@ @@@@@ @@@@ @@@@ @@@ @@@@@@@ @@@ @@@@ @@@@ @@@@@@ @@@@ @@@ @@@@@@@ // 1@@@@@@|\ \___/) @@1@@@@@2 ~~~ ~~~~~ @@@@ ~~@@ ~~~ ~~~~~~~~~~~ ~~~~ ~~~~ ~~~~~~~~~~~ ~@@ @@@@@ // 2@@@@@ | \ \ / | @@@@@@2 @@@ @@@@@ @@@@ @@@@ @@@ @@@@@@@@@@@ @@@@@@@@@ @@@@ @@@@@@@@@@@ @@@@@@@@@ @@@@@ // 2@@@@ |_ > <|__ @@1@12 @@@ @@@@@ @@@@ @@@@ @@@ @@@@ @@@@@@ @@@@ @@@@ @@@@ @@@@@@ @@@ @@@@@@@ // @@@@ / _| / \/ \ @@1@ @@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@@ @@@@ @@@@ @@@@ @@@@@ @@@@@@@@@ @@@@ @@@@ // @@ / |^\/ | | @@1 @@@ @@@@ @@@@ @@@ @@@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@@@@@@ @@@@ @@@@ // / / ---- \ \\\= @@ @@@@@@@@@@@@@@@@@@@@@@ // \___/ -------- ~~ @@@ // @@ | | | | -- @@ // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // SPDX-License-Identifier: MIT pragma solidity ^0.8.21; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { IInfinexFaucet } from "src/infinex-faucet/IInfinexFaucet.sol"; import { EIP712 } from "src/accounts/storage/EIP712.sol"; import { Ownable } from "src/ownership/Ownable.sol"; import { UUPSImplementation } from "src/proxy/UUPSImplementation.sol"; import { InfinexFaucetStorage } from "src/infinex-faucet/InfinexFaucetStorage.sol"; contract InfinexFaucet is IInfinexFaucet, Ownable, UUPSImplementation, Initializable { using SafeERC20 for IERC20; using ECDSA for bytes32; bytes32 internal constant _CLAIM_REQUEST_TYPEHASH = keccak256( "ClaimRequest(address recipientAddress,uint256 claimAmount,address tokenAddress,uint256 tokenAmount,uint256 nonce,uint256 timestamp)" ); /*/////////////////////////////////////////////////////////////// CONSTRUCTOR ///////////////////////////////////////////////////////////////*/ constructor() Ownable(address(this)) { _disableInitializers(); } /*/////////////////////////////////////////////////////////////// INITIALIZER ///////////////////////////////////////////////////////////////*/ /** * @notice Initializes the contract. * @param _claimSigner The address of the claim signer. * @param _maxEthPerClaim The maximum amount of ETH that can be claimed per claim. * @param _signatureExpiryTime The expiry time for the claim signature. * @param _feeCollector The address of the fee collector. */ function initialize(address _claimSigner, uint256 _maxEthPerClaim, uint256 _signatureExpiryTime, address _feeCollector) external initializer { if (_claimSigner == address(0)) revert ZeroAddress(); if (_maxEthPerClaim == 0) revert ZeroAmount(); if (_signatureExpiryTime == 0) revert ZeroExpiry(); if (_feeCollector == address(0)) revert ZeroAddress(); EIP712.__EIP712_init("InfinexFaucet", "1"); InfinexFaucetStorage._setClaimSigner(_claimSigner); InfinexFaucetStorage._setMaxEthPerClaim(_maxEthPerClaim); InfinexFaucetStorage._setSignatureExpiryTime(_signatureExpiryTime); InfinexFaucetStorage._setFeeCollector(_feeCollector); } /** * @notice Returns the address that signs the claim requests. * @return The address of the claim signer. */ function getClaimSigner() external view returns (address) { return InfinexFaucetStorage._getClaimSigner(); } /** * @notice Returns the maximum amount of ETH that can be claimed per claim. * @return The maximum amount of ETH that can be claimed per claim. */ function getMaxEthPerClaim() external view returns (uint256) { return InfinexFaucetStorage._getMaxEthPerClaim(); } /** * @notice Returns the address that collects the fees. * @return The address of the fee collector. */ function getFeeCollector() external view returns (address) { return InfinexFaucetStorage._getFeeCollector(); } /** * @notice Checks if a nonce has been claimed. * @param _nonce The nonce to check. * @return Whether the nonce has been claimed. */ function isClaimedNonce(uint256 _nonce) external view returns (bool) { return InfinexFaucetStorage._isClaimedNonce(_nonce); } /** * @notice Returns the expiry time for the claim signature * @return The expiry time for the claim signature */ function getSignatureExpiryTime() external view returns (uint256) { return InfinexFaucetStorage._getSignatureExpiryTime(); } /** * @notice Claims ETH from the faucet. * @param _request The claim request. * @param _claimSignature The claim signature. * @dev the tokenAddress is optional, if it's zero, the claim is for ETH with no token fee sent to the feeCollector. */ function claimEth(ClaimRequest calldata _request, bytes calldata _claimSignature) external { if (_request.claimAmount == 0 || _request.claimAmount > InfinexFaucetStorage._getMaxEthPerClaim()) revert InvalidClaimAmount(); if (InfinexFaucetStorage._isClaimedNonce(_request.nonce)) revert InvalidNonce(); if (address(this).balance < _request.claimAmount) revert InvalidBalance(); if (_request.timestamp > block.timestamp) revert InvalidTimestamp(); if (block.timestamp > _request.timestamp + InfinexFaucetStorage._getSignatureExpiryTime()) revert RequestExpired(); if (recoverClaimSigner(_request, _claimSignature) != InfinexFaucetStorage._getClaimSigner()) revert InvalidSignature(); emit ClaimSuccessful( _request.recipientAddress, _request.claimAmount, _request.tokenAddress, _request.tokenAmount, _request.nonce, _request.timestamp ); InfinexFaucetStorage._consumeNonce(_request.nonce); if (_request.tokenAddress != address(0)) { if (_request.tokenAmount != IERC20(_request.tokenAddress).allowance(_request.recipientAddress, address(this))) { revert InvalidTokenAllowance(); } // slither-disable-next-line arbitrary-send-erc20 IERC20(_request.tokenAddress).safeTransferFrom( _request.recipientAddress, InfinexFaucetStorage._getFeeCollector(), _request.tokenAmount ); } // slither-disable-next-line arbitrary-send-eth (bool success,) = _request.recipientAddress.call{ value: _request.claimAmount }(""); if (!success) revert EtherTransferFailed(); } /** * @notice Sets the claim signer. * @param _newClaimSigner The new claim signer. * @dev Only the owner can set the claim signer. */ function setClaimSigner(address _newClaimSigner) external onlyOwner { if (_newClaimSigner == address(0)) revert ZeroAddress(); emit ClaimSignerSet(_newClaimSigner); InfinexFaucetStorage._setClaimSigner(_newClaimSigner); } /** * @notice Sets the maximum amount of ETH that can be claimed per claim. * @param _newMaxEthPerClaim The new maximum amount of ETH that can be claimed per claim. * @dev Only the owner can set the maximum amount of ETH that can be claimed per claim. */ function setMaxEthPerClaim(uint256 _newMaxEthPerClaim) external onlyOwner { if (_newMaxEthPerClaim == 0) revert ZeroAmount(); emit MaxEthPerClaimSet(_newMaxEthPerClaim); InfinexFaucetStorage._setMaxEthPerClaim(_newMaxEthPerClaim); } /** * @notice Sets the address that collects the fees. * @param _newFeeCollector The new fee collector. * @dev Only the owner can set the fee collector. */ function setFeeCollector(address _newFeeCollector) external onlyOwner { if (_newFeeCollector == address(0)) revert ZeroAddress(); emit FeeCollectorSet(_newFeeCollector); InfinexFaucetStorage._setFeeCollector(_newFeeCollector); } /** * @notice Sets the expiry time for the claim signature * @param _newExpiryTime The new expiry time for the claim signature * @dev Only the owner can set the expiry time for the claim signature. */ function setSignatureExpiryTime(uint256 _newExpiryTime) external onlyOwner { if (_newExpiryTime == 0) revert ZeroExpiry(); emit SignatureExpiryTimeSet(_newExpiryTime); InfinexFaucetStorage._setSignatureExpiryTime(_newExpiryTime); } /** * @notice Recovers the claim signer from the claim request and the claim request signature. * @param _request The claim request. * @param _claimRequestSignature The claim request signature. * @return The address of the claim signer. */ function recoverClaimSigner(ClaimRequest calldata _request, bytes calldata _claimRequestSignature) public view returns (address) { return ECDSA.recover( EIP712._hashTypedDataV4( keccak256( abi.encode( _CLAIM_REQUEST_TYPEHASH, _request.recipientAddress, _request.claimAmount, _request.tokenAddress, _request.tokenAmount, _request.nonce, _request.timestamp ) ) ), _claimRequestSignature ); } /** * @notice Withdraws the specified amount of ETH to the owner. * @param _amount The amount of ETH to withdraw. * @dev Only the owner can withdraw the ETH. */ function withdrawEthToOwner(uint256 _amount) external onlyOwner { emit WithdrawEth(_owner(), _amount); (bool success,) = _owner().call{ value: _amount }(""); if (!success) revert EtherTransferFailed(); } /** * @notice Withdraws the specified amount of the specified ERC20 token to the owner. * @param _tokenAddress The address of the ERC20 token. * @param _amount The amount of the ERC20 token to withdraw. * @dev Only the owner can withdraw the ERC20 token. */ function withdrawERC20ToOwner(address _tokenAddress, uint256 _amount) external onlyOwner { emit WithdrawERC20(_tokenAddress, _amount); IERC20(_tokenAddress).safeTransfer(_owner(), _amount); } /** * @notice Upgrades the contract to a new implementation. * @param _newImplementation The address of the new implementation. * @dev Only the owner can upgrade the contract. */ function upgradeTo(address _newImplementation) external onlyOwner { _upgradeTo(_newImplementation); } /** * @notice Allows the contract to receive ETH. */ receive() external payable { emit Deposit(msg.sender, msg.value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @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 value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @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. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ 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]. * * CAUTION: See Security Considerations above. */ 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 v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../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; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @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); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // 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 FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { uint256 localValue = value; 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_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal * representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @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 } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile 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 {MessageHashUtils-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] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { 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. /// @solidity memory-safe-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 { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile 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 {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); 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] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // 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, s); } // 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, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @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, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) pragma solidity ^0.8.20; import {Strings} from "../Strings.sol"; /** * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. * * The library provides methods for generating a hash of a message that conforms to the * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] * specifications. */ library MessageHashUtils { /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing a bytes32 `messageHash` with * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * keccak256, although any bytes32 value can be safely used because the final digest will * be re-hashed. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) } } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing an arbitrary `message` with * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { return keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x00` (data with intended validator). * * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended * `validator` address. Then hashing the result. * * See {ECDSA-recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked(hex"19_00", validator, data)); } /** * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). * * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with * `\x19\x01` and hashing the result. It corresponds to the hash signed by the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. * * See {ECDSA-recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, hex"19_01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) digest := keccak256(ptr, 0x42) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.21; import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; library EIP712 { bytes32 private constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /// @custom:storage-location erc7201:openzeppelin.storage.EIP712 struct EIP712Storage { /// @custom:oz-renamed-from _HASHED_NAME bytes32 _hashedName; /// @custom:oz-renamed-from _HASHED_VERSION bytes32 _hashedVersion; string _name; string _version; } function _getEIP712Storage() private pure returns (EIP712Storage storage data) { bytes32 s = keccak256(abi.encode("io.infinex.EIP712")); assembly { data.slot := s } } /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ function __EIP712_init(string memory name, string memory version) internal { __EIP712_init_unchained(name, version); } function __EIP712_init_unchained(string memory name, string memory version) internal { EIP712Storage storage $ = _getEIP712Storage(); $._name = name; $._version = version; // Reset prior values in storage if upgrading $._hashedName = 0; $._hashedVersion = 0; } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { return _buildDomainSeparator(); } function _buildDomainSeparator() private view returns (bytes32) { return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view returns (bytes32) { return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev See {IERC-5267}. */ function eip712Domain() internal view returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { EIP712Storage storage $ = _getEIP712Storage(); // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized // and the EIP712 domain is not reliable, as it will be missing name and version. // solhint-disable-next-line gas-custom-errors require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized"); return ( hex"0f", // 01111 _EIP712Name(), _EIP712Version(), block.chainid, address(this), bytes32(0), new uint256[](0) ); } /** * @dev The name parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712Name() internal view returns (string memory) { EIP712Storage storage $ = _getEIP712Storage(); return $._name; } /** * @dev The version parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712Version() internal view returns (string memory) { EIP712Storage storage $ = _getEIP712Storage(); return $._version; } /** * @dev The hash of the name parameter for the EIP712 domain. * * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead. */ function _EIP712NameHash() internal view returns (bytes32) { EIP712Storage storage $ = _getEIP712Storage(); string memory name = _EIP712Name(); if (bytes(name).length > 0) { return keccak256(bytes(name)); } else { // If the name is empty, the contract may have been upgraded without initializing the new storage. // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design. bytes32 hashedName = $._hashedName; if (hashedName != 0) { return hashedName; } else { return keccak256(""); } } } /** * @dev The hash of the version parameter for the EIP712 domain. * * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead. */ function _EIP712VersionHash() internal view returns (bytes32) { EIP712Storage storage $ = _getEIP712Storage(); string memory version = _EIP712Version(); if (bytes(version).length > 0) { return keccak256(bytes(version)); } else { // If the version is empty, the contract may have been upgraded without initializing the new storage. // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design. bytes32 hashedVersion = $._hashedVersion; if (hashedVersion != 0) { return hashedVersion; } else { return keccak256(""); } } } }
// c=< // | // | ////\ 1@2 // @@ | /___\** @@@2 @@@@@@@@@@@@@@@@@@@@@@ // @@@ | |~L~ |* @@@@@@ @@@ @@@@@ @@@@ @@@ @@@@ @@@ @@@@@@@@ @@@@ @@@@ @@@ @@@@@@@@@ @@@@ @@@@ // @@@@@ | \=_/8 @@@@1@@ @@@ @@@@@ @@@@ @@@@ @@@ @@@@@ @@@ @@@@@@@@@ @@@@ @@@@@ @@@@ @@@@@@@@@ @@@@ @@@@ // @@@@@@| _ /| |\__ @@@@@@@@2 @@@ @@@@@ @@@@ @@@@ @@@ @@@@@@@ @@@ @@@@ @@@@ @@@@@@ @@@@ @@@ @@@@@@@ // 1@@@@@@|\ \___/) @@1@@@@@2 ~~~ ~~~~~ @@@@ ~~@@ ~~~ ~~~~~~~~~~~ ~~~~ ~~~~ ~~~~~~~~~~~ ~@@ @@@@@ // 2@@@@@ | \ \ / | @@@@@@2 @@@ @@@@@ @@@@ @@@@ @@@ @@@@@@@@@@@ @@@@@@@@@ @@@@ @@@@@@@@@@@ @@@@@@@@@ @@@@@ // 2@@@@ |_ > <|__ @@1@12 @@@ @@@@@ @@@@ @@@@ @@@ @@@@ @@@@@@ @@@@ @@@@ @@@@ @@@@@@ @@@ @@@@@@@ // @@@@ / _| / \/ \ @@1@ @@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@@ @@@@ @@@@ @@@@ @@@@@ @@@@@@@@@ @@@@ @@@@ // @@ / |^\/ | | @@1 @@@ @@@@ @@@@ @@@ @@@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@@@@@@ @@@@ @@@@ // / / ---- \ \\\= @@ @@@@@@@@@@@@@@@@@@@@@@ // \___/ -------- ~~ @@@ // @@ | | | | -- @@ // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // SPDX-Licence-Identifier: MIT pragma solidity ^0.8.21; /* * @title IInfinexFaucet * @notice Interface for the InfinexFaucet contract. */ interface IInfinexFaucet { /*/////////////////////////////////////////////////////////////// STRUCTS ///////////////////////////////////////////////////////////////*/ struct ClaimRequest { address recipientAddress; uint256 claimAmount; address tokenAddress; uint256 tokenAmount; uint256 nonce; uint256 timestamp; } /*/////////////////////////////////////////////////////////////// EVENTS ///////////////////////////////////////////////////////////////*/ event ClaimSuccessful( address _recipientAddress, uint256 _claimAmount, address _tokenAddress, uint256 _tokenAmount, uint256 _nonce, uint256 _timestamp ); event ClaimSignerSet(address _newClaimSigner); event MaxEthPerClaimSet(uint256 _newMaxEthPerClaim); event FeeCollectorSet(address _newFeeCollector); event SignatureExpiryTimeSet(uint256 _newExpiryTime); event WithdrawEth(address _recipient, uint256 _amount); event WithdrawERC20(address _tokenAddress, uint256 _amount); event Deposit(address _sender, uint256 _amount); /*/////////////////////////////////////////////////////////////// ERRORS ///////////////////////////////////////////////////////////////*/ error EtherTransferFailed(); error InvalidClaimAmount(); error InvalidTokenAllowance(); error InvalidTimestamp(); error InvalidNonce(); error InvalidBalance(); error RequestExpired(); error InvalidSignature(); error ZeroExpiry(); error ZeroAmount(); /*/////////////////////////////////////////////////////////////// VIEW FUNCTIONS ///////////////////////////////////////////////////////////////*/ /** * @notice Returns the claim signer. * @return The claim signer. */ function getClaimSigner() external view returns (address); /** * @notice Returns the maximum amount of ETH that can be claimed per claim. * @return The maximum amount of ETH that can be claimed per claim. */ function getMaxEthPerClaim() external view returns (uint256); /** * @notice Returns the address that collects the fees. * @return The address of the fee collector. */ function getFeeCollector() external view returns (address); /** * @notice Checks if a nonce has been claimed. * @param _nonce The nonce to check. * @return Whether the nonce has been claimed. */ function isClaimedNonce(uint256 _nonce) external view returns (bool); /** * @notice Returns the expiry time for the claim signature * @return The expiry time for the claim signature */ function getSignatureExpiryTime() external view returns (uint256); /*/////////////////////////////////////////////////////////////// MUTATIVE FUNCTIONS ///////////////////////////////////////////////////////////////*/ /** * @notice Initializes the contract. * @param _claimSigner The address of the claim signer. * @param _maxEthPerClaim The maximum amount of ETH that can be claimed per claim. * @param _signatureExpiryTime The expiry time for the claim signature in seconds. * @param _feeCollector The address of the fee collector. */ function initialize(address _claimSigner, uint256 _maxEthPerClaim, uint256 _signatureExpiryTime, address _feeCollector) external; /** * @notice Claims ETH from the faucet. * @param _request The claim request. * @param _claimSignature The claim signature. */ function claimEth(ClaimRequest calldata _request, bytes calldata _claimSignature) external; /** * @notice Sets the claim signer. * @param _newClaimSigner The new claim signer. */ function setClaimSigner(address _newClaimSigner) external; /** * @notice Sets the maximum amount of ETH that can be claimed per claim. * @param _newMaxEthPerClaim The new maximum amount of ETH that can be claimed per claim. */ function setMaxEthPerClaim(uint256 _newMaxEthPerClaim) external; /** * @notice Sets the address that collects the fees. * @param _newFeeCollector The new fee collector. */ function setFeeCollector(address _newFeeCollector) external; /** * @notice Sets the expiry time for the claim signature * @param _newExpiryTime The new expiry time for the claim signature */ function setSignatureExpiryTime(uint256 _newExpiryTime) external; /** * @notice Recovers the claim signer, from the claim signature. * @param _request The claim request. * @param _claimSignature The claim signature. * @return The claim signer. */ function recoverClaimSigner(ClaimRequest calldata _request, bytes calldata _claimSignature) external view returns (address); /** * @notice Withdraws the specified amount of ETH to the owner. * @param _amount The amount of ETH to withdraw. */ function withdrawEthToOwner(uint256 _amount) external; /** * @notice Withdraws the specified amount of the specified ERC20 token to the owner. * @param _tokenAddress The address of the ERC20 token. * @param _amount The amount of the ERC20 token to withdraw. */ function withdrawERC20ToOwner(address _tokenAddress, uint256 _amount) external; }
// c=< // | // | ////\ 1@2 // @@ | /___\** @@@2 @@@@@@@@@@@@@@@@@@@@@@ // @@@ | |~L~ |* @@@@@@ @@@ @@@@@ @@@@ @@@ @@@@ @@@ @@@@@@@@ @@@@ @@@@ @@@ @@@@@@@@@ @@@@ @@@@ // @@@@@ | \=_/8 @@@@1@@ @@@ @@@@@ @@@@ @@@@ @@@ @@@@@ @@@ @@@@@@@@@ @@@@ @@@@@ @@@@ @@@@@@@@@ @@@@ @@@@ // @@@@@@| _ /| |\__ @@@@@@@@2 @@@ @@@@@ @@@@ @@@@ @@@ @@@@@@@ @@@ @@@@ @@@@ @@@@@@ @@@@ @@@ @@@@@@@ // 1@@@@@@|\ \___/) @@1@@@@@2 ~~~ ~~~~~ @@@@ ~~@@ ~~~ ~~~~~~~~~~~ ~~~~ ~~~~ ~~~~~~~~~~~ ~@@ @@@@@ // 2@@@@@ | \ \ / | @@@@@@2 @@@ @@@@@ @@@@ @@@@ @@@ @@@@@@@@@@@ @@@@@@@@@ @@@@ @@@@@@@@@@@ @@@@@@@@@ @@@@@ // 2@@@@ |_ > <|__ @@1@12 @@@ @@@@@ @@@@ @@@@ @@@ @@@@ @@@@@@ @@@@ @@@@ @@@@ @@@@@@ @@@ @@@@@@@ // @@@@ / _| / \/ \ @@1@ @@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@@ @@@@ @@@@ @@@@ @@@@@ @@@@@@@@@ @@@@ @@@@ // @@ / |^\/ | | @@1 @@@ @@@@ @@@@ @@@ @@@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@@@@@@ @@@@ @@@@ // / / ---- \ \\\= @@ @@@@@@@@@@@@@@@@@@@@@@ // \___/ -------- ~~ @@@ // @@ | | | | -- @@ // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // SPDX-Licence-Identifier: MIT pragma solidity ^0.8.21; library InfinexFaucetStorage { bytes32 internal constant STORAGE_SLOT = keccak256("infinex.faucet.storage"); /** * @notice Storage structure for the InfinexFaucet contract. * @param claimSigner The address that signs the claim requests. * @param feeCollector The address that collects the fees.` * @param claimedNonces Mapping storing all claimed nonces. * @param signatureExpiryTime The expiry time for the claim signature in seconds. */ struct Data { address claimSigner; address feeCollector; uint256 maxEthPerClaim; mapping(uint248 => uint256) claimedNonces; uint256 signatureExpiryTime; } /** * @notice Returns the storage data struct. * @return ds The storage data struct. */ function getStorage() internal pure returns (Data storage ds) { bytes32 slot = STORAGE_SLOT; assembly { ds.slot := slot } } /** * @notice Returns the signer that signs the claim requests. * @return The address of the claim signer. */ function _getClaimSigner() internal view returns (address) { return getStorage().claimSigner; } /** * @notice Returns the address that collects the fees. * @return The address of the fee collector. */ function _getFeeCollector() internal view returns (address) { return getStorage().feeCollector; } /** * @notice Returns the maximum amount of ETH that can be claimed per claim. * @return The maximum amount of ETH that can be claimed per claim. */ function _getMaxEthPerClaim() internal view returns (uint256) { return getStorage().maxEthPerClaim; } /** * @notice Checks if a nonce has been claimed. * @param _nonce The nonce to check. * @return Whether the nonce has been claimed. */ function _isClaimedNonce(uint256 _nonce) internal view returns (bool) { uint256 bitmap = getStorage().claimedNonces[uint248(_nonce >> 8)]; return (bitmap & (1 << (_nonce & 0xFF))) != 0; } /** * @notice Returns the expiry time for the claim signature in seconds. * @return The expiry time for the claim signature in seconds. */ function _getSignatureExpiryTime() internal view returns (uint256) { return getStorage().signatureExpiryTime; } /** * @notice Sets the signer that signs the claim requests. * @param _signer The address of the claim signer. */ function _setClaimSigner(address _signer) internal { getStorage().claimSigner = _signer; } /** * @notice Sets the maximum amount of ETH that can be claimed per claim. * @param _maxEthPerClaim The maximum amount of ETH that can be claimed per claim. */ function _setMaxEthPerClaim(uint256 _maxEthPerClaim) internal { getStorage().maxEthPerClaim = _maxEthPerClaim; } /** * @notice Sets the address that collects the fees. * @param _collector The address of the fee collector. */ function _setFeeCollector(address _collector) internal { getStorage().feeCollector = _collector; } /** * @notice Sets the expiry in seconds for the claim signature. * @param _expiry The expiry time for the claim signature in seconds. */ function _setSignatureExpiryTime(uint256 _expiry) internal { getStorage().signatureExpiryTime = _expiry; } /** * @notice Consumes a nonce, marking it as used * @param _nonce The nonce to consume */ function _consumeNonce(uint256 _nonce) internal { getStorage().claimedNonces[uint248(_nonce >> 8)] |= (1 << (_nonce & 0xFF)); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.21; library AddressUtil { function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.21; /** * @title Contract for facilitating ownership by a single address, using the Openzeppelin ownable upgradable storage slot. * @dev Implementation is a modified version of Openzeppelin and Synthetix Ownable implementations. */ interface IOwnable { /*/////////////////////////////////////////////////////////////// ERRORS ///////////////////////////////////////////////////////////////*/ /** * @notice Thrown when an address tries to accept ownership but has not been nominated. * @param addr The address that is trying to accept ownership. */ error NotNominated(address addr); /** * @notice Thrown when an address tries to renounce pending ownership but not owner or pending owner. * @param addr The address that is trying to renounce pending ownership. */ error NotNominatedOrOwner(address addr); /** * @notice Thrown when an address is zero. */ error ZeroAddress(); /** * @notice Thrown when no change is made. */ error NoChange(); /** * @notice Thrown when an address is not the owner. */ error Unauthorized(address addr); /*/////////////////////////////////////////////////////////////// EVENTS ///////////////////////////////////////////////////////////////*/ /** * @notice Emitted when an address has been nominated. * @param newOwner The address that has been nominated. */ event OwnerNominated(address newOwner); /** * @notice Emitted when the owner of the contract has changed. * @param oldOwner The previous owner of the contract. * @param newOwner The new owner of the contract. */ event OwnerChanged(address oldOwner, address newOwner); /** * @notice Emitted when the nominated pending owner renounces themselves as nominated. * @param pendingOwner The pending owner that is renounced. */ event PendingOwnerRenounced(address pendingOwner); /*/////////////////////////////////////////////////////////////// VIEW FUNCTIONS ///////////////////////////////////////////////////////////////*/ /** * @notice Returns the current owner of the contract. */ function owner() external view returns (address); /** * @notice Returns the current pending owner of the contract. * @dev Only one address can be pending at a time. */ function pendingOwner() external view returns (address); /*/////////////////////////////////////////////////////////////// MUTATIVE FUNCTIONS ///////////////////////////////////////////////////////////////*/ /** * @notice Allows a pending owner address to accept ownership of the contract. * @dev Reverts if the caller has not been nominated. */ function acceptOwnership() external; /** * @notice Allows the current owner to nominate a new owner. * @dev The pending owner will have to call `acceptOwnership` in a separate transaction in order to finalize the action and become the new contract owner. * @param newOwner The address that is to become nominated. */ function transferOwnership(address newOwner) external; /** * @notice Allows a pending owner to reject the nomination. */ function renouncePendingOwnership() external; /** * @notice Allows the current owner of the contract to renounce the ownership and pending ownership completely. */ function renounceOwnership() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.21; import { IOwnable } from "src/ownership/IOwnable.sol"; import { OwnableStorage } from "src/ownership/OwnableStorage.sol"; /** * @title Contract for facilitating ownership by a single address, using the Openzeppelin ownable upgradable storage slot. * @dev Implementation is a modified version of Openzeppelin and Synthetix Ownable implementations. */ abstract contract Ownable is IOwnable { /*/////////////////////////////////////////////////////////////// CONSTRUCTOR ///////////////////////////////////////////////////////////////*/ constructor(address initialOwner) { if (initialOwner == address(0)) revert ZeroAddress(); OwnableStorage._setOwner(initialOwner); } /*/////////////////////////////////////////////////////////////// MODIFIERS ///////////////////////////////////////////////////////////////*/ /** * @notice Reverts if the caller is not the owner. */ modifier onlyOwner() { _onlyOwner(); _; } /*/////////////////////////////////////////////////////////////// VIEW FUNCTIONS ///////////////////////////////////////////////////////////////*/ /** * @notice Returns the current owner of the contract. */ function owner() external view virtual returns (address) { return _owner(); } /** * @notice Returns the current pending owner of the contract. * @dev Only one address can be pending at a time. */ function pendingOwner() external view virtual returns (address) { return _pendingOwner(); } /*/////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS ///////////////////////////////////////////////////////////////*/ /** * @notice Returns the current owner of the contract. */ function _owner() internal view virtual returns (address) { return OwnableStorage._getOwner(); } /** * @notice Returns the current pending owner of the contract. * @dev Only one address can be nominated at a time. */ function _pendingOwner() internal view virtual returns (address) { return OwnableStorage._getPendingOwner(); } /** * @notice Reverts if the caller is not the owner. */ function _onlyOwner() internal view virtual { if (msg.sender != _owner()) { revert Unauthorized(msg.sender); } } /*/////////////////////////////////////////////////////////////// MUTATIVE FUNCTIONS ///////////////////////////////////////////////////////////////*/ /** * @notice Allows a pending owner to accept ownership of the contract. * @dev Reverts if the caller is not pending owner. */ function acceptOwnership() public virtual { address currentPendingOwner = _pendingOwner(); if (msg.sender != currentPendingOwner) { revert NotNominated(msg.sender); } emit OwnerChanged(_owner(), currentPendingOwner); OwnableStorage._setOwner(currentPendingOwner); OwnableStorage._setPendingOwner(address(0)); } /** * @notice Allows the current owner to nominate a new owner. * @dev The pending owner will have to call `acceptOwnership` in a separate transaction in order to finalize the action and become the new contract owner. * @param newOwner The address that is to become nominated. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert ZeroAddress(); } if (newOwner == _pendingOwner()) { revert NoChange(); } emit OwnerNominated(newOwner); OwnableStorage._setPendingOwner(newOwner); } /** * @notice Allows a pending owner or owner to reject the nomination. */ function renouncePendingOwnership() external virtual { address pendingOwner = _pendingOwner(); if (pendingOwner != msg.sender && msg.sender != _owner()) { revert NotNominatedOrOwner(msg.sender); } emit PendingOwnerRenounced(pendingOwner); OwnableStorage._setPendingOwner(address(0)); } /** * @notice Allows the current owner of the contract to renounce the ownership and pending ownership completely. */ function renounceOwnership() public virtual onlyOwner { emit PendingOwnerRenounced(_pendingOwner()); OwnableStorage._setPendingOwner(address(0)); emit OwnerChanged(_owner(), address(0)); OwnableStorage._setOwner(address(0)); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.21; /** * @title Ownable Storage * @dev This library provides storage and functions for managing the ownership of a contract, using the Openzeppelin ownable upgradable storage slot. * Implementation is a modified version of Openzeppelin and Synthetix Ownable implementations. */ library OwnableStorage { // Storage slot copied from the Openzeppelin ownable upgradable contract. // https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/OwnableUpgradeable.sol // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant _SLOT_OWNABLE_STORAGE = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; //#gitleaks:allow struct Storage { address _owner; address _pendingOwner; } /** * @notice Loads the storage data for the Ownable contract * @return store The storage data for the Ownable contract */ function getStorage() internal pure returns (Storage storage store) { bytes32 s = _SLOT_OWNABLE_STORAGE; assembly { store.slot := s } } /** * @notice Returns the current owner of the contract * @return The address of the owner */ function _getOwner() internal view returns (address) { return getStorage()._owner; } /** * @notice Returns the pending owner of the contract. * @return The address of the pending owner. */ function _getPendingOwner() internal view returns (address) { return getStorage()._pendingOwner; } /** * @notice Sets the owner in ownable storage. * @param _newOwner The new owner of the contract. */ function _setOwner(address _newOwner) internal { getStorage()._owner = _newOwner; } /** * @notice Sets the pending owner. * @param _newOwner The new pending owner of the contract. */ function _setPendingOwner(address _newOwner) internal { getStorage()._pendingOwner = _newOwner; } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.21; /** * @title Contract to be used as the implementation of a Universal Upgradeable Proxy Standard (UUPS) proxy. * Important: A UUPS proxy requires its upgradeability functions to be in the implementation as opposed to the proxy. * This means that if the proxy is upgraded to an implementation that does not support this interface, it will no longer be upgradeable. * Copied from Synthetix * https://github.com/Synthetixio/synthetix-v3/blob/main/utils/core-contracts/contracts/interfaces/IUUPSImplementation.sol */ interface IUUPSImplementation { /** * @notice Thrown when an incoming implementation will not be able to receive future upgrades. */ error ImplementationIsSterile(address implementation); /** * @notice Thrown intentionally when testing future upgradeability of an implementation. */ error UpgradeSimulationFailed(); /** * @notice Thrown when the address is zero. */ error NullAddress(); /** * @notice Thrown when the address is not a contract. */ error NotAContract(address implementation); /** * @notice Thrown when the implementation is the same as the current implementation. */ error SameImplementation(); /** * @notice Emitted when the implementation of the proxy has been upgraded. * @param self The address of the proxy whose implementation was upgraded. * @param implementation The address of the proxy's new implementation. */ event Upgraded(address indexed self, address implementation); /** * @notice Allows the proxy to be upgraded to a new implementation. * @param newImplementation The address of the proxy's new implementation. * @dev Will revert if `newImplementation` is not upgradeable. * @dev The implementation of this function needs to be protected by some sort of access control such as `onlyOwner`. */ function upgradeTo(address newImplementation) external; /** * @notice Function used to determine if a new implementation will be able to receive future upgrades in `upgradeTo`. * @param newImplementation The address of the new implementation being tested for future upgradeability. * @dev This function will always revert, but will revert with different error messages. The function `upgradeTo` uses this error to determine the future upgradeability of the implementation in question. */ function simulateUpgradeTo(address newImplementation) external; /** * @notice Retrieves the current implementation of the proxy. * @return The address of the current implementation. */ function getImplementation() external view returns (address); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.21; /** * @dev Modified Synthetix proxy storage contract. * https://github.com/Synthetixio/synthetix-v3/blob/main/utils/core-contracts/contracts/proxy/ProxyStorage.sol */ contract ProxyStorage { // keccak256(abi.encode("io.synthetix.core-contracts.Proxy")); bytes32 private constant _SLOT_PROXY_STORAGE = 0x5a648c35a2f5512218b4683cf10e03f5b7c9dc7346e1bf77d304ae97f60f592b; //#gitleaks:allow struct ProxyStore { address implementation; bool simulatingUpgrade; } function _proxyStore() internal pure returns (ProxyStore storage store) { bytes32 s = _SLOT_PROXY_STORAGE; assembly { store.slot := s } } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.21; import { IUUPSImplementation } from "src/proxy/IUUPSImplementation.sol"; import { AddressUtil } from "src/libraries/AddressUtil.sol"; import { ProxyStorage } from "src/proxy/ProxyStorage.sol"; /** * @dev Modified Synthetix UUPS Implementation contract * https://github.com/Synthetixio/synthetix-v3/blob/main/utils/core-contracts/contracts/proxy/UUPSImplementation.sol */ abstract contract UUPSImplementation is IUUPSImplementation, ProxyStorage { /** * @notice Function used to determine if a new implementation will be able to receive future upgrades in `upgradeTo`. * @param newImplementation The address of the new implementation being tested for future upgradeability. * @dev This function will always revert, but will revert with different error messages. The function `upgradeTo` uses this error to determine the future upgradeability of the implementation in question. */ function simulateUpgradeTo(address newImplementation) public override { if (newImplementation == address(0)) { revert NullAddress(); } ProxyStore storage store = _proxyStore(); store.simulatingUpgrade = true; address currentImplementation = store.implementation; store.implementation = newImplementation; // slither-disable-start controlled-delegatecall // solhint-disable-next-line avoid-low-level-calls (bool rollbackSuccessful,) = newImplementation.delegatecall(abi.encodeCall(this.upgradeTo, (currentImplementation))); // slither-disable-end controlled-delegatecall if (!rollbackSuccessful || _proxyStore().implementation != currentImplementation) { revert UpgradeSimulationFailed(); } store.simulatingUpgrade = false; // solhint-disable-next-line reason-string,gas-custom-errors revert(); } /** * @notice Retrieves the current implementation of the proxy. * @return The address of the current implementation. */ function getImplementation() external view override returns (address) { return _proxyStore().implementation; } /** * @notice Allows the proxy to be upgraded to a new implementation. * @param newImplementation The address of the new implementation. * @dev Will revert if `newImplementation` is not upgradeable. * @dev The implementation of this function needs to be protected by some sort of access control such as `onlyOwner`. */ function _upgradeTo(address newImplementation) internal virtual { if (newImplementation == address(0)) { revert NullAddress(); } if (!AddressUtil.isContract(newImplementation)) { revert NotAContract(newImplementation); } ProxyStore storage store = _proxyStore(); if (newImplementation == store.implementation) { revert SameImplementation(); } if (!store.simulatingUpgrade && _implementationIsSterile(newImplementation)) { revert ImplementationIsSterile(newImplementation); } store.implementation = newImplementation; emit Upgraded(address(this), newImplementation); } /** * @notice Checks if the candidate implementation is sterile. * @param candidateImplementation The address of the candidate implementation. * @return True if the candidate implementation is sterile, false otherwise. */ function _implementationIsSterile(address candidateImplementation) internal virtual returns (bool) { (bool simulationReverted, bytes memory simulationResponse) = // solhint-disable-next-line avoid-low-level-calls address(this).delegatecall(abi.encodeCall(this.simulateUpgradeTo, (candidateImplementation))); return !simulationReverted && keccak256(abi.encodePacked(simulationResponse)) == keccak256(abi.encodePacked(UpgradeSimulationFailed.selector)); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "shanghai", "remappings": [ "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity/", "@synthetixio/core-contracts/=node_modules/@synthetixio/core-contracts/", "@synthetixio/core-modules/=node_modules/@synthetixio/core-modules/", "@synthetixio/main/=node_modules/@synthetixio/main/", "@synthetixio/oracle-manager/=node_modules/@synthetixio/oracle-manager/", "@synthetixio/perps-market/=node_modules/@synthetixio/perps-market/", "@synthetixio/spot-market/=node_modules/@synthetixio/spot-market/", "ERC721A/=lib/ERC721A/contracts/", "cannon-std/=lib/cannon-std/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "wormhole-circle-integration/=lib/wormhole-circle-integration/evm/src/", "wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/", "wormhole/=lib/wormhole-circle-integration/evm/src/" ], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"EtherTransferFailed","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ImplementationIsSterile","type":"error"},{"inputs":[],"name":"InvalidBalance","type":"error"},{"inputs":[],"name":"InvalidClaimAmount","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidTimestamp","type":"error"},{"inputs":[],"name":"InvalidTokenAllowance","type":"error"},{"inputs":[],"name":"NoChange","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"NotAContract","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"NotNominated","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"NotNominatedOrOwner","type":"error"},{"inputs":[],"name":"NullAddress","type":"error"},{"inputs":[],"name":"RequestExpired","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SameImplementation","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UpgradeSimulationFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"inputs":[],"name":"ZeroExpiry","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newClaimSigner","type":"address"}],"name":"ClaimSignerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_recipientAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_claimAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"_tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"ClaimSuccessful","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newFeeCollector","type":"address"}],"name":"FeeCollectorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newMaxEthPerClaim","type":"uint256"}],"name":"MaxEthPerClaimSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"PendingOwnerRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newExpiryTime","type":"uint256"}],"name":"SignatureExpiryTimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"self","type":"address"},{"indexed":false,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"WithdrawERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"WithdrawEth","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipientAddress","type":"address"},{"internalType":"uint256","name":"claimAmount","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IInfinexFaucet.ClaimRequest","name":"_request","type":"tuple"},{"internalType":"bytes","name":"_claimSignature","type":"bytes"}],"name":"claimEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getClaimSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxEthPerClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSignatureExpiryTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_claimSigner","type":"address"},{"internalType":"uint256","name":"_maxEthPerClaim","type":"uint256"},{"internalType":"uint256","name":"_signatureExpiryTime","type":"uint256"},{"internalType":"address","name":"_feeCollector","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"isClaimedNonce","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[{"components":[{"internalType":"address","name":"recipientAddress","type":"address"},{"internalType":"uint256","name":"claimAmount","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IInfinexFaucet.ClaimRequest","name":"_request","type":"tuple"},{"internalType":"bytes","name":"_claimRequestSignature","type":"bytes"}],"name":"recoverClaimSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renouncePendingOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newClaimSigner","type":"address"}],"name":"setClaimSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newFeeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaxEthPerClaim","type":"uint256"}],"name":"setMaxEthPerClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newExpiryTime","type":"uint256"}],"name":"setSignatureExpiryTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"simulateUpgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawERC20ToOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawEthToOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
608060405234801561000f575f80fd5b50308061002f5760405163d92e233d60e01b815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b0319166001600160a01b03831617905550610072610077565b610129565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100c75760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146101265780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61207e80620001375f395ff3fe60806040526004361061013f575f3560e01c80638da5cb5b116100b3578063c7f62cda1161006d578063c7f62cda14610376578063e30c397814610395578063e8b736d2146103a9578063f2fde38b146103c8578063f96bd15e146103e7578063fed80d43146103fb575f80fd5b80638da5cb5b146102e85780638e4e210c146102fc5780639ced959c146103105780639e3d87cd14610324578063a42dce8014610343578063aaf10f4214610362575f80fd5b80635d8e8e12116101045780635d8e8e1214610234578063715018a61461025357806379ba5097146102675780637c4668211461027b5780637f9a6d3e146102aa57806386c93914146102c9575f80fd5b8063107bf3041461018257806312fde4b7146101a35780633659cfe6146101d4578063429889e5146101f357806342b8627014610215575f80fd5b3661017e57604080513381523460208201527fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c910160405180910390a1005b5f80fd5b34801561018d575f80fd5b506101a161019c366004611c85565b61041a565b005b3480156101ae575f80fd5b506101b76104e7565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101df575f80fd5b506101a16101ee366004611cb7565b6104f5565b3480156101fe575f80fd5b50610207610509565b6040519081526020016101cb565b348015610220575f80fd5b506101a161022f366004611cd0565b61051f565b34801561023f575f80fd5b506101a161024e366004611cf8565b610585565b34801561025e575f80fd5b506101a1610929565b348015610272575f80fd5b506101a16109d2565b348015610286575f80fd5b5061029a610295366004611c85565b610a6a565b60405190151581526020016101cb565b3480156102b5575f80fd5b506101b76102c4366004611cf8565b610a7a565b3480156102d4575f80fd5b506101a16102e3366004611c85565b610b76565b3480156102f3575f80fd5b506101b7610be6565b348015610307575f80fd5b50610207610bef565b34801561031b575f80fd5b506101a1610c05565b34801561032f575f80fd5b506101a161033e366004611d7b565b610ca7565b34801561034e575f80fd5b506101a161035d366004611cb7565b610ec1565b34801561036d575f80fd5b506101b7610f35565b348015610381575f80fd5b506101a1610390366004611cb7565b610f54565b3480156103a0575f80fd5b506101b761108f565b3480156103b4575f80fd5b506101a16103c3366004611c85565b611098565b3480156103d3575f80fd5b506101a16103e2366004611cb7565b611108565b3480156103f2575f80fd5b506101b76111b5565b348015610406575f80fd5b506101a1610415366004611cb7565b6111be565b610422611232565b7fccbd99ba6da8f29b2a4f65e474e3c3973564d356c162c08d45f3dc7f0cb5b3aa61044b61126d565b604080516001600160a01b039092168252602082018490520160405180910390a15f61047561126d565b6001600160a01b0316826040515f6040518083038185875af1925050503d805f81146104bc576040519150601f19603f3d011682016040523d82523d5f602084013e6104c1565b606091505b50509050806104e357604051630ce8f45160e31b815260040160405180910390fd5b5050565b5f6104f0611276565b905090565b6104fd611232565b61050681611298565b50565b5f6104f05f805160206120098339815191525490565b610527611232565b604080516001600160a01b0384168152602081018390527fbe7426aee8a34d0263892b55ce65ce81d8f4c806eb4719e59015ea49feb92d22910160405180910390a16104e361057461126d565b6001600160a01b03841690836113bf565b602083013515806105a757505f80516020612009833981519152548360200135115b156105c55760405163843ce46b60e01b815260040160405180910390fd5b6105d28360800135611423565b156105f057604051633ab3447f60e11b815260040160405180910390fd5b82602001354710156106155760405163c52e3eff60e01b815260040160405180910390fd5b428360a00135111561063a5760405163b7d0949760e01b815260040160405180910390fd5b5f80516020612029833981519152546106579060a0850135611dbe565b42111561067757604051637f780e6960e11b815260040160405180910390fd5b61067f611466565b6001600160a01b0316610693848484610a7a565b6001600160a01b0316146106ba57604051638baa579f60e01b815260040160405180910390fd5b7f76834c89377a1e213631260da07a4661f86fc426236d9e550e89e5aca9b76f466106e86020850185611cb7565b60208501356106fd6060870160408801611cb7565b604080516001600160a01b0394851681526020810193909352921681830152606086810135908201526080808701359082015260a0808701359082015290519081900360c00190a16080830135600881901c5f9081527f0e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb7910420368a602052604090208054600160ff9093169290921b90911790555f61079e6060850160408601611cb7565b6001600160a01b0316146108a3576107bc6060840160408501611cb7565b6001600160a01b031663dd62ed3e6107d76020860186611cb7565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa15801561081f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108439190611ddd565b8360600135146108665760405163ae21117560e01b815260040160405180910390fd5b6108a36108766020850185611cb7565b61087e611276565b606086018035906108929060408901611cb7565b6001600160a01b031692919061147a565b5f6108b16020850185611cb7565b6001600160a01b031684602001356040515f6040518083038185875af1925050503d805f81146108fc576040519150601f19603f3d011682016040523d82523d5f602084013e610901565b606091505b505090508061092357604051630ce8f45160e31b815260040160405180910390fd5b50505050565b610931611232565b7ff4907248db93a98ecbc4159d3185003476dd4e957f364d6a150074c34778cae061095a6114b3565b6040516001600160a01b03909116815260200160405180910390a161097e5f6114bc565b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c6109a761126d565b604080516001600160a01b0390921682525f60208301520160405180910390a16109d05f6114f0565b565b5f6109db6114b3565b9050336001600160a01b03821614610a0d5760405163a0e5a0d760e01b81523360048201526024015b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c610a3661126d565b604080516001600160a01b03928316815291841660208301520160405180910390a1610a61816114f0565b6105065f6114bc565b5f610a7482611423565b92915050565b5f610b6c610b317fe5fb3b4ff4aa6f8a2b3b64f831cc29072ed4359b7debd39df09efa6e4b7eaec9610aaf6020880188611cb7565b6020880135610ac460608a0160408b01611cb7565b6040805160208101959095526001600160a01b03938416908501526060808501929092529091166080808401919091529088013560a0808401919091529088013560c083015287013560e08201526101000160405160208183030381529060405280519060200120611521565b84848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061154d92505050565b90505b9392505050565b610b7e611232565b805f03610b9e5760405163022ee6c960e21b815260040160405180910390fd5b6040518181527fa6956cec979426e24ab5322f3701cdd2fa75561d0bc54af41b405b89f0394b629060200160405180910390a1610506815f8051602061202983398151915255565b5f6104f061126d565b5f6104f05f805160206120298339815191525490565b5f610c0e6114b3565b90506001600160a01b0381163314801590610c425750610c2c61126d565b6001600160a01b0316336001600160a01b031614155b15610c6257604051637bce25d560e11b8152336004820152602401610a04565b6040516001600160a01b03821681527ff4907248db93a98ecbc4159d3185003476dd4e957f364d6a150074c34778cae09060200160405180910390a16105065f6114bc565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f81158015610cec5750825b90505f8267ffffffffffffffff166001148015610d085750303b155b905081158015610d16575080155b15610d345760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610d5e57845460ff60401b1916600160401b1785555b6001600160a01b038916610d855760405163d92e233d60e01b815260040160405180910390fd5b875f03610da557604051631f2a200560e01b815260040160405180910390fd5b865f03610dc55760405163022ee6c960e21b815260040160405180910390fd5b6001600160a01b038616610dec5760405163d92e233d60e01b815260040160405180910390fd5b610e346040518060400160405280600d81526020016c125b999a5b995e11985d58d95d609a1b815250604051806040016040528060018152602001603160f81b815250611575565b610e3d8961157f565b610e52885f8051602061200983398151915255565b610e67875f8051602061202983398151915255565b610e7086611593565b8315610eb657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610ec9611232565b6001600160a01b038116610ef05760405163d92e233d60e01b815260040160405180910390fd5b6040516001600160a01b03821681527f12e1d17016b94668449f97876f4a8d5cc2c19f314db337418894734037cc19d49060200160405180910390a161050681611593565b5f5f80516020611fc98339815191525b546001600160a01b0316919050565b6001600160a01b038116610f7b5760405163e99d5ac560e01b815260040160405180910390fd5b5f80516020611fc983398151915280546001600160a01b038381166001600160a81b031983168117600160a01b1784556040805192909316602480840182905284518085039091018152604490930184526020830180516001600160e01b0316631b2ce7f360e11b17905292515f92610ff391611df4565b5f60405180830381855af49150503d805f811461102b576040519150601f19603f3d011682016040523d82523d5f602084013e611030565b606091505b5050905080158061106157506001600160a01b0382165f80516020611fc9833981519152546001600160a01b031614155b1561107f57604051631439f4b560e31b815260040160405180910390fd5b825460ff60a01b191683555f8080fd5b5f6104f06114b3565b6110a0611232565b805f036110c057604051631f2a200560e01b815260040160405180910390fd5b6040518181527f6ca6204691c3fef45094960351f2b7c7dd1dbdfbfcebd6f65cdacf99f524abce9060200160405180910390a1610506815f8051602061200983398151915255565b611110611232565b6001600160a01b0381166111375760405163d92e233d60e01b815260040160405180910390fd5b61113f6114b3565b6001600160a01b0316816001600160a01b0316036111705760405163a88ee57760e01b815260040160405180910390fd5b6040516001600160a01b03821681527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a1610506816114bc565b5f6104f0611466565b6111c6611232565b6001600160a01b0381166111ed5760405163d92e233d60e01b815260040160405180910390fd5b6040516001600160a01b03821681527f69112557338804187f030e6bc258c3623b16425c8ce94d6f28a7ee92fc7f31419060200160405180910390a16105068161157f565b61123a61126d565b6001600160a01b0316336001600160a01b0316146109d05760405163472511eb60e11b8152336004820152602401610a04565b5f6104f06115a7565b5f5f80516020611fa98339815191525b600101546001600160a01b0316919050565b6001600160a01b0381166112bf5760405163e99d5ac560e01b815260040160405180910390fd5b803b6112e9576040516322a2d07b60e21b81526001600160a01b0382166004820152602401610a04565b5f80516020611fc983398151915280546001600160a01b039081169083160361132557604051634c3b76bf60e01b815260040160405180910390fd5b8054600160a01b900460ff161580156113425750611342826115bb565b1561136b57604051631550430160e01b81526001600160a01b0383166004820152602401610a04565b80546001600160a01b0319166001600160a01b038316908117825560405190815230907f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c79060200160405180910390a25050565b6040516001600160a01b0383811660248301526044820183905261141e91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506116b6565b505050565b600881901c5f9081527f0e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb7910420368a6020526040902054600160ff9092169190911b16151590565b5f5f80516020611fa9833981519152610f45565b6040516001600160a01b0384811660248301528381166044830152606482018390526109239186918216906323b872dd906084016113ec565b5f6104f0611717565b805f80516020611fe98339815191525b60010180546001600160a01b0319166001600160a01b039290921691909117905550565b805f80516020611fe98339815191525b80546001600160a01b0319166001600160a01b039290921691909117905550565b5f610a7461152d61172b565b8360405161190160f01b8152600281019290925260228201526042902090565b5f805f8061155b8686611734565b92509250925061156b828261177d565b5090949350505050565b6104e38282611835565b805f80516020611fa9833981519152611500565b805f80516020611fa98339815191526114cc565b5f5f80516020611fe9833981519152610f45565b604080516001600160a01b03831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166363fb166d60e11b17905290515f918291829130916116119190611df4565b5f60405180830381855af49150503d805f8114611649576040519150601f19603f3d011682016040523d82523d5f602084013e61164e565b606091505b5091509150811580156116ae5750604051631439f4b560e31b602082015260240160405160208183030381529060405280519060200120816040516020016116969190611df4565b60405160208183030381529060405280519060200120145b949350505050565b5f6116ca6001600160a01b0384168361186c565b905080515f141580156116ee5750808060200190518101906116ec9190611e20565b155b1561141e57604051635274afe760e01b81526001600160a01b0384166004820152602401610a04565b5f5f80516020611fe9833981519152611286565b5f6104f0611879565b5f805f835160410361176b576020840151604085015160608601515f1a61175d888285856118ec565b955095509550505050611776565b505081515f91506002905b9250925092565b5f82600381111561179057611790611e3f565b03611799575050565b60018260038111156117ad576117ad611e3f565b036117cb5760405163f645eedf60e01b815260040160405180910390fd5b60028260038111156117df576117df611e3f565b036118005760405163fce698f760e01b815260048101829052602401610a04565b600382600381111561181457611814611e3f565b036104e3576040516335e2f38360e21b815260048101829052602401610a04565b5f61183e6119b4565b90506002810161184e8482611eec565b506003810161185d8382611eec565b505f8082556001909101555050565b6060610b6f83835f611a09565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6118a3611aa2565b6118ab611b07565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111561192557505f915060039050826119aa565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611976573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381166119a157505f9250600191508290506119aa565b92505f91508190505b9450945094915050565b5f806040516020016119eb9060208082526011908201527034b79734b73334b732bc1722a4a81b989960791b604082015260600190565b60408051601f19818403018152919052805160209091012092915050565b606081471015611a2e5760405163cd78605960e01b8152306004820152602401610a04565b5f80856001600160a01b03168486604051611a499190611df4565b5f6040518083038185875af1925050503d805f8114611a83576040519150601f19603f3d011682016040523d82523d5f602084013e611a88565b606091505b5091509150611a98868383611b46565b9695505050505050565b5f80611aac6119b4565b90505f611ab7611ba2565b805190915015611acf57805160209091012092915050565b81548015611ade579392505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470935050505090565b5f80611b116119b4565b90505f611b1c611c40565b805190915015611b3457805160209091012092915050565b60018201548015611ade579392505050565b606082611b5b57611b5682611c5c565b610b6f565b8151158015611b7257506001600160a01b0384163b155b15611b9b57604051639996b31560e01b81526001600160a01b0385166004820152602401610a04565b5080610b6f565b60605f611bad6119b4565b9050806002018054611bbe90611e67565b80601f0160208091040260200160405190810160405280929190818152602001828054611bea90611e67565b8015611c355780601f10611c0c57610100808354040283529160200191611c35565b820191905f5260205f20905b815481529060010190602001808311611c1857829003601f168201915b505050505091505090565b60605f611c4b6119b4565b9050806003018054611bbe90611e67565b805115611c6c5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5f60208284031215611c95575f80fd5b5035919050565b80356001600160a01b0381168114611cb2575f80fd5b919050565b5f60208284031215611cc7575f80fd5b610b6f82611c9c565b5f8060408385031215611ce1575f80fd5b611cea83611c9c565b946020939093013593505050565b5f805f83850360e0811215611d0b575f80fd5b60c0811215611d18575f80fd5b5083925060c084013567ffffffffffffffff80821115611d36575f80fd5b818601915086601f830112611d49575f80fd5b813581811115611d57575f80fd5b876020828501011115611d68575f80fd5b6020830194508093505050509250925092565b5f805f8060808587031215611d8e575f80fd5b611d9785611c9c565b93506020850135925060408501359150611db360608601611c9c565b905092959194509250565b80820180821115610a7457634e487b7160e01b5f52601160045260245ffd5b5f60208284031215611ded575f80fd5b5051919050565b5f82515f5b81811015611e135760208186018101518583015201611df9565b505f920191825250919050565b5f60208284031215611e30575f80fd5b81518015158114610b6f575f80fd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680611e7b57607f821691505b602082108103611e9957634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561141e575f81815260208120601f850160051c81016020861015611ec55750805b601f850160051c820191505b81811015611ee457828155600101611ed1565b505050505050565b815167ffffffffffffffff811115611f0657611f06611e53565b611f1a81611f148454611e67565b84611e9f565b602080601f831160018114611f4d575f8415611f365750858301515b5f19600386901b1c1916600185901b178555611ee4565b5f85815260208120601f198616915b82811015611f7b57888601518255948401946001909101908401611f5c565b5085821015611f9857878501515f19600388901b60f8161c191681555b5050505050600190811b0190555056fe0e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb791042036875a648c35a2f5512218b4683cf10e03f5b7c9dc7346e1bf77d304ae97f60f592b9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993000e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb791042036890e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb7910420368ba26469706673582212202285021427e3c856fc82d7e3f0deccd93eae87439597551bef699c1ebc2a846f64736f6c63430008150033
Deployed Bytecode
0x60806040526004361061013f575f3560e01c80638da5cb5b116100b3578063c7f62cda1161006d578063c7f62cda14610376578063e30c397814610395578063e8b736d2146103a9578063f2fde38b146103c8578063f96bd15e146103e7578063fed80d43146103fb575f80fd5b80638da5cb5b146102e85780638e4e210c146102fc5780639ced959c146103105780639e3d87cd14610324578063a42dce8014610343578063aaf10f4214610362575f80fd5b80635d8e8e12116101045780635d8e8e1214610234578063715018a61461025357806379ba5097146102675780637c4668211461027b5780637f9a6d3e146102aa57806386c93914146102c9575f80fd5b8063107bf3041461018257806312fde4b7146101a35780633659cfe6146101d4578063429889e5146101f357806342b8627014610215575f80fd5b3661017e57604080513381523460208201527fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c910160405180910390a1005b5f80fd5b34801561018d575f80fd5b506101a161019c366004611c85565b61041a565b005b3480156101ae575f80fd5b506101b76104e7565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101df575f80fd5b506101a16101ee366004611cb7565b6104f5565b3480156101fe575f80fd5b50610207610509565b6040519081526020016101cb565b348015610220575f80fd5b506101a161022f366004611cd0565b61051f565b34801561023f575f80fd5b506101a161024e366004611cf8565b610585565b34801561025e575f80fd5b506101a1610929565b348015610272575f80fd5b506101a16109d2565b348015610286575f80fd5b5061029a610295366004611c85565b610a6a565b60405190151581526020016101cb565b3480156102b5575f80fd5b506101b76102c4366004611cf8565b610a7a565b3480156102d4575f80fd5b506101a16102e3366004611c85565b610b76565b3480156102f3575f80fd5b506101b7610be6565b348015610307575f80fd5b50610207610bef565b34801561031b575f80fd5b506101a1610c05565b34801561032f575f80fd5b506101a161033e366004611d7b565b610ca7565b34801561034e575f80fd5b506101a161035d366004611cb7565b610ec1565b34801561036d575f80fd5b506101b7610f35565b348015610381575f80fd5b506101a1610390366004611cb7565b610f54565b3480156103a0575f80fd5b506101b761108f565b3480156103b4575f80fd5b506101a16103c3366004611c85565b611098565b3480156103d3575f80fd5b506101a16103e2366004611cb7565b611108565b3480156103f2575f80fd5b506101b76111b5565b348015610406575f80fd5b506101a1610415366004611cb7565b6111be565b610422611232565b7fccbd99ba6da8f29b2a4f65e474e3c3973564d356c162c08d45f3dc7f0cb5b3aa61044b61126d565b604080516001600160a01b039092168252602082018490520160405180910390a15f61047561126d565b6001600160a01b0316826040515f6040518083038185875af1925050503d805f81146104bc576040519150601f19603f3d011682016040523d82523d5f602084013e6104c1565b606091505b50509050806104e357604051630ce8f45160e31b815260040160405180910390fd5b5050565b5f6104f0611276565b905090565b6104fd611232565b61050681611298565b50565b5f6104f05f805160206120098339815191525490565b610527611232565b604080516001600160a01b0384168152602081018390527fbe7426aee8a34d0263892b55ce65ce81d8f4c806eb4719e59015ea49feb92d22910160405180910390a16104e361057461126d565b6001600160a01b03841690836113bf565b602083013515806105a757505f80516020612009833981519152548360200135115b156105c55760405163843ce46b60e01b815260040160405180910390fd5b6105d28360800135611423565b156105f057604051633ab3447f60e11b815260040160405180910390fd5b82602001354710156106155760405163c52e3eff60e01b815260040160405180910390fd5b428360a00135111561063a5760405163b7d0949760e01b815260040160405180910390fd5b5f80516020612029833981519152546106579060a0850135611dbe565b42111561067757604051637f780e6960e11b815260040160405180910390fd5b61067f611466565b6001600160a01b0316610693848484610a7a565b6001600160a01b0316146106ba57604051638baa579f60e01b815260040160405180910390fd5b7f76834c89377a1e213631260da07a4661f86fc426236d9e550e89e5aca9b76f466106e86020850185611cb7565b60208501356106fd6060870160408801611cb7565b604080516001600160a01b0394851681526020810193909352921681830152606086810135908201526080808701359082015260a0808701359082015290519081900360c00190a16080830135600881901c5f9081527f0e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb7910420368a602052604090208054600160ff9093169290921b90911790555f61079e6060850160408601611cb7565b6001600160a01b0316146108a3576107bc6060840160408501611cb7565b6001600160a01b031663dd62ed3e6107d76020860186611cb7565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa15801561081f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108439190611ddd565b8360600135146108665760405163ae21117560e01b815260040160405180910390fd5b6108a36108766020850185611cb7565b61087e611276565b606086018035906108929060408901611cb7565b6001600160a01b031692919061147a565b5f6108b16020850185611cb7565b6001600160a01b031684602001356040515f6040518083038185875af1925050503d805f81146108fc576040519150601f19603f3d011682016040523d82523d5f602084013e610901565b606091505b505090508061092357604051630ce8f45160e31b815260040160405180910390fd5b50505050565b610931611232565b7ff4907248db93a98ecbc4159d3185003476dd4e957f364d6a150074c34778cae061095a6114b3565b6040516001600160a01b03909116815260200160405180910390a161097e5f6114bc565b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c6109a761126d565b604080516001600160a01b0390921682525f60208301520160405180910390a16109d05f6114f0565b565b5f6109db6114b3565b9050336001600160a01b03821614610a0d5760405163a0e5a0d760e01b81523360048201526024015b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c610a3661126d565b604080516001600160a01b03928316815291841660208301520160405180910390a1610a61816114f0565b6105065f6114bc565b5f610a7482611423565b92915050565b5f610b6c610b317fe5fb3b4ff4aa6f8a2b3b64f831cc29072ed4359b7debd39df09efa6e4b7eaec9610aaf6020880188611cb7565b6020880135610ac460608a0160408b01611cb7565b6040805160208101959095526001600160a01b03938416908501526060808501929092529091166080808401919091529088013560a0808401919091529088013560c083015287013560e08201526101000160405160208183030381529060405280519060200120611521565b84848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061154d92505050565b90505b9392505050565b610b7e611232565b805f03610b9e5760405163022ee6c960e21b815260040160405180910390fd5b6040518181527fa6956cec979426e24ab5322f3701cdd2fa75561d0bc54af41b405b89f0394b629060200160405180910390a1610506815f8051602061202983398151915255565b5f6104f061126d565b5f6104f05f805160206120298339815191525490565b5f610c0e6114b3565b90506001600160a01b0381163314801590610c425750610c2c61126d565b6001600160a01b0316336001600160a01b031614155b15610c6257604051637bce25d560e11b8152336004820152602401610a04565b6040516001600160a01b03821681527ff4907248db93a98ecbc4159d3185003476dd4e957f364d6a150074c34778cae09060200160405180910390a16105065f6114bc565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f81158015610cec5750825b90505f8267ffffffffffffffff166001148015610d085750303b155b905081158015610d16575080155b15610d345760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610d5e57845460ff60401b1916600160401b1785555b6001600160a01b038916610d855760405163d92e233d60e01b815260040160405180910390fd5b875f03610da557604051631f2a200560e01b815260040160405180910390fd5b865f03610dc55760405163022ee6c960e21b815260040160405180910390fd5b6001600160a01b038616610dec5760405163d92e233d60e01b815260040160405180910390fd5b610e346040518060400160405280600d81526020016c125b999a5b995e11985d58d95d609a1b815250604051806040016040528060018152602001603160f81b815250611575565b610e3d8961157f565b610e52885f8051602061200983398151915255565b610e67875f8051602061202983398151915255565b610e7086611593565b8315610eb657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610ec9611232565b6001600160a01b038116610ef05760405163d92e233d60e01b815260040160405180910390fd5b6040516001600160a01b03821681527f12e1d17016b94668449f97876f4a8d5cc2c19f314db337418894734037cc19d49060200160405180910390a161050681611593565b5f5f80516020611fc98339815191525b546001600160a01b0316919050565b6001600160a01b038116610f7b5760405163e99d5ac560e01b815260040160405180910390fd5b5f80516020611fc983398151915280546001600160a01b038381166001600160a81b031983168117600160a01b1784556040805192909316602480840182905284518085039091018152604490930184526020830180516001600160e01b0316631b2ce7f360e11b17905292515f92610ff391611df4565b5f60405180830381855af49150503d805f811461102b576040519150601f19603f3d011682016040523d82523d5f602084013e611030565b606091505b5050905080158061106157506001600160a01b0382165f80516020611fc9833981519152546001600160a01b031614155b1561107f57604051631439f4b560e31b815260040160405180910390fd5b825460ff60a01b191683555f8080fd5b5f6104f06114b3565b6110a0611232565b805f036110c057604051631f2a200560e01b815260040160405180910390fd5b6040518181527f6ca6204691c3fef45094960351f2b7c7dd1dbdfbfcebd6f65cdacf99f524abce9060200160405180910390a1610506815f8051602061200983398151915255565b611110611232565b6001600160a01b0381166111375760405163d92e233d60e01b815260040160405180910390fd5b61113f6114b3565b6001600160a01b0316816001600160a01b0316036111705760405163a88ee57760e01b815260040160405180910390fd5b6040516001600160a01b03821681527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a1610506816114bc565b5f6104f0611466565b6111c6611232565b6001600160a01b0381166111ed5760405163d92e233d60e01b815260040160405180910390fd5b6040516001600160a01b03821681527f69112557338804187f030e6bc258c3623b16425c8ce94d6f28a7ee92fc7f31419060200160405180910390a16105068161157f565b61123a61126d565b6001600160a01b0316336001600160a01b0316146109d05760405163472511eb60e11b8152336004820152602401610a04565b5f6104f06115a7565b5f5f80516020611fa98339815191525b600101546001600160a01b0316919050565b6001600160a01b0381166112bf5760405163e99d5ac560e01b815260040160405180910390fd5b803b6112e9576040516322a2d07b60e21b81526001600160a01b0382166004820152602401610a04565b5f80516020611fc983398151915280546001600160a01b039081169083160361132557604051634c3b76bf60e01b815260040160405180910390fd5b8054600160a01b900460ff161580156113425750611342826115bb565b1561136b57604051631550430160e01b81526001600160a01b0383166004820152602401610a04565b80546001600160a01b0319166001600160a01b038316908117825560405190815230907f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c79060200160405180910390a25050565b6040516001600160a01b0383811660248301526044820183905261141e91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506116b6565b505050565b600881901c5f9081527f0e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb7910420368a6020526040902054600160ff9092169190911b16151590565b5f5f80516020611fa9833981519152610f45565b6040516001600160a01b0384811660248301528381166044830152606482018390526109239186918216906323b872dd906084016113ec565b5f6104f0611717565b805f80516020611fe98339815191525b60010180546001600160a01b0319166001600160a01b039290921691909117905550565b805f80516020611fe98339815191525b80546001600160a01b0319166001600160a01b039290921691909117905550565b5f610a7461152d61172b565b8360405161190160f01b8152600281019290925260228201526042902090565b5f805f8061155b8686611734565b92509250925061156b828261177d565b5090949350505050565b6104e38282611835565b805f80516020611fa9833981519152611500565b805f80516020611fa98339815191526114cc565b5f5f80516020611fe9833981519152610f45565b604080516001600160a01b03831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166363fb166d60e11b17905290515f918291829130916116119190611df4565b5f60405180830381855af49150503d805f8114611649576040519150601f19603f3d011682016040523d82523d5f602084013e61164e565b606091505b5091509150811580156116ae5750604051631439f4b560e31b602082015260240160405160208183030381529060405280519060200120816040516020016116969190611df4565b60405160208183030381529060405280519060200120145b949350505050565b5f6116ca6001600160a01b0384168361186c565b905080515f141580156116ee5750808060200190518101906116ec9190611e20565b155b1561141e57604051635274afe760e01b81526001600160a01b0384166004820152602401610a04565b5f5f80516020611fe9833981519152611286565b5f6104f0611879565b5f805f835160410361176b576020840151604085015160608601515f1a61175d888285856118ec565b955095509550505050611776565b505081515f91506002905b9250925092565b5f82600381111561179057611790611e3f565b03611799575050565b60018260038111156117ad576117ad611e3f565b036117cb5760405163f645eedf60e01b815260040160405180910390fd5b60028260038111156117df576117df611e3f565b036118005760405163fce698f760e01b815260048101829052602401610a04565b600382600381111561181457611814611e3f565b036104e3576040516335e2f38360e21b815260048101829052602401610a04565b5f61183e6119b4565b90506002810161184e8482611eec565b506003810161185d8382611eec565b505f8082556001909101555050565b6060610b6f83835f611a09565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6118a3611aa2565b6118ab611b07565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111561192557505f915060039050826119aa565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611976573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381166119a157505f9250600191508290506119aa565b92505f91508190505b9450945094915050565b5f806040516020016119eb9060208082526011908201527034b79734b73334b732bc1722a4a81b989960791b604082015260600190565b60408051601f19818403018152919052805160209091012092915050565b606081471015611a2e5760405163cd78605960e01b8152306004820152602401610a04565b5f80856001600160a01b03168486604051611a499190611df4565b5f6040518083038185875af1925050503d805f8114611a83576040519150601f19603f3d011682016040523d82523d5f602084013e611a88565b606091505b5091509150611a98868383611b46565b9695505050505050565b5f80611aac6119b4565b90505f611ab7611ba2565b805190915015611acf57805160209091012092915050565b81548015611ade579392505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470935050505090565b5f80611b116119b4565b90505f611b1c611c40565b805190915015611b3457805160209091012092915050565b60018201548015611ade579392505050565b606082611b5b57611b5682611c5c565b610b6f565b8151158015611b7257506001600160a01b0384163b155b15611b9b57604051639996b31560e01b81526001600160a01b0385166004820152602401610a04565b5080610b6f565b60605f611bad6119b4565b9050806002018054611bbe90611e67565b80601f0160208091040260200160405190810160405280929190818152602001828054611bea90611e67565b8015611c355780601f10611c0c57610100808354040283529160200191611c35565b820191905f5260205f20905b815481529060010190602001808311611c1857829003601f168201915b505050505091505090565b60605f611c4b6119b4565b9050806003018054611bbe90611e67565b805115611c6c5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5f60208284031215611c95575f80fd5b5035919050565b80356001600160a01b0381168114611cb2575f80fd5b919050565b5f60208284031215611cc7575f80fd5b610b6f82611c9c565b5f8060408385031215611ce1575f80fd5b611cea83611c9c565b946020939093013593505050565b5f805f83850360e0811215611d0b575f80fd5b60c0811215611d18575f80fd5b5083925060c084013567ffffffffffffffff80821115611d36575f80fd5b818601915086601f830112611d49575f80fd5b813581811115611d57575f80fd5b876020828501011115611d68575f80fd5b6020830194508093505050509250925092565b5f805f8060808587031215611d8e575f80fd5b611d9785611c9c565b93506020850135925060408501359150611db360608601611c9c565b905092959194509250565b80820180821115610a7457634e487b7160e01b5f52601160045260245ffd5b5f60208284031215611ded575f80fd5b5051919050565b5f82515f5b81811015611e135760208186018101518583015201611df9565b505f920191825250919050565b5f60208284031215611e30575f80fd5b81518015158114610b6f575f80fd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680611e7b57607f821691505b602082108103611e9957634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561141e575f81815260208120601f850160051c81016020861015611ec55750805b601f850160051c820191505b81811015611ee457828155600101611ed1565b505050505050565b815167ffffffffffffffff811115611f0657611f06611e53565b611f1a81611f148454611e67565b84611e9f565b602080601f831160018114611f4d575f8415611f365750858301515b5f19600386901b1c1916600185901b178555611ee4565b5f85815260208120601f198616915b82811015611f7b57888601518255948401946001909101908401611f5c565b5085821015611f9857878501515f19600388901b60f8161c191681555b5050505050600190811b0190555056fe0e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb791042036875a648c35a2f5512218b4683cf10e03f5b7c9dc7346e1bf77d304ae97f60f592b9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993000e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb791042036890e8219ac12e929de1b6033b3e2732b6b926bdd19cf6c423e2dffb7910420368ba26469706673582212202285021427e3c856fc82d7e3f0deccd93eae87439597551bef699c1ebc2a846f64736f6c63430008150033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ 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.