Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
11984379 | 313 days ago | 0.25 ETH |
Loading...
Loading
Contract Name:
PimlicoERC20Paymaster
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; // Import the required libraries and contracts import "@account-abstraction/contracts/core/BasePaymaster.sol"; import "@account-abstraction/contracts/core/Helpers.sol"; import "@account-abstraction/contracts/interfaces/UserOperation.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import "./interfaces/IOracle.sol"; import "@account-abstraction/contracts/core/EntryPoint.sol"; import "./utils/SafeTransferLib.sol"; /// @title PimlicoERC20Paymaster /// @notice An ERC-4337 Paymaster contract by Pimlico which is able to sponsor gas fees in exchange for ERC20 tokens. /// The contract refunds excess tokens if the actual gas cost is lower than the initially provided amount. /// It also allows updating price configuration and withdrawing tokens by the contract owner. /// The contract uses an Oracle to fetch the latest token prices. /// @dev Inherits from BasePaymaster. contract PimlicoERC20Paymaster is BasePaymaster { uint256 public constant priceDenominator = 1e6; uint256 public constant REFUND_POSTOP_COST = 40000; // Estimated gas cost for refunding tokens after the transaction is completed // The token, tokenOracle, and nativeAssetOracle are declared as immutable, // meaning their values cannot change after contract creation. IERC20 public immutable token; // The ERC20 token used for transaction fee payments uint256 public immutable tokenDecimals; IOracle public immutable tokenOracle; // The Oracle contract used to fetch the latest token prices IOracle public immutable nativeAssetOracle; // The Oracle contract used to fetch the latest ETH prices uint192 public previousPrice; // The cached token price from the Oracle uint32 public priceMarkup; // The price markup percentage applied to the token price (1e6 = 100%) uint32 public priceUpdateThreshold; // The price update threshold percentage that triggers a price update (1e6 = 100%) event ConfigUpdated(uint32 priceMarkup, uint32 updateThreshold); event UserOperationSponsored(address indexed user, uint256 actualTokenNeeded, uint256 actualGasCost); /// @notice Initializes the PimlicoERC20Paymaster contract with the given parameters. /// @param _token The ERC20 token used for transaction fee payments. /// @param _entryPoint The EntryPoint contract used in the Account Abstraction infrastructure. /// @param _tokenOracle The Oracle contract used to fetch the latest token prices. /// @param _nativeAssetOracle The Oracle contract used to fetch the latest native asset (ETH, Matic, Avax, etc.) prices. /// @param _owner The address that will be set as the owner of the contract. constructor( IERC20Metadata _token, IEntryPoint _entryPoint, IOracle _tokenOracle, IOracle _nativeAssetOracle, address _owner ) BasePaymaster(_entryPoint) { token = _token; tokenOracle = _tokenOracle; // oracle for token -> usd nativeAssetOracle = _nativeAssetOracle; // oracle for native asset(eth/matic/avax..) -> usd priceMarkup = 110e4; // 110% 1e6 = 100% priceUpdateThreshold = 25e3; // 2.5% 1e6 = 100% transferOwnership(_owner); tokenDecimals = 10 ** _token.decimals(); require(_tokenOracle.decimals() == 8, "PP-ERC20 : token oracle decimals must be 8"); require(_nativeAssetOracle.decimals() == 8, "PP-ERC20 : native asset oracle decimals must be 8"); } /// @notice Updates the price markup and price update threshold configurations. /// @param _priceMarkup The new price markup percentage (1e6 = 100%). /// @param _updateThreshold The new price update threshold percentage (1e6 = 100%). function updateConfig(uint32 _priceMarkup, uint32 _updateThreshold) external onlyOwner { require(_priceMarkup <= 120e4, "PP-ERC20 : price markup too high"); require(_priceMarkup >= 1e6, "PP-ERC20 : price markeup too low"); require(_updateThreshold <= 1e6, "PP-ERC20 : update threshold too high"); priceMarkup = _priceMarkup; priceUpdateThreshold = _updateThreshold; emit ConfigUpdated(_priceMarkup, _updateThreshold); } /// @notice Allows the contract owner to withdraw a specified amount of tokens from the contract. /// @param to The address to transfer the tokens to. /// @param amount The amount of tokens to transfer. function withdrawToken(address to, uint256 amount) external onlyOwner { SafeTransferLib.safeTransfer(address(token), to, amount); } /// @notice Updates the token price by fetching the latest price from the Oracle. function updatePrice() external { // This function updates the cached ERC20/ETH price ratio uint192 tokenPrice = fetchPrice(tokenOracle); uint192 nativeAssetPrice = fetchPrice(nativeAssetOracle); previousPrice = nativeAssetPrice * uint192(tokenDecimals) / tokenPrice; } /// @notice Validates a paymaster user operation and calculates the required token amount for the transaction. /// @param userOp The user operation data. /// @param requiredPreFund The amount of tokens required for pre-funding. /// @return context The context containing the token amount and user sender address (if applicable). /// @return validationResult A uint256 value indicating the result of the validation (always 0 in this implementation). function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32, uint256 requiredPreFund) internal override returns (bytes memory context, uint256 validationResult) { unchecked { uint256 cachedPrice = previousPrice; require(cachedPrice != 0, "PP-ERC20 : price not set"); uint256 length = userOp.paymasterAndData.length - 20; // 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdf is the mask for the last 6 bits 011111 which mean length should be 100000(32) || 000000(0) require( length & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdf == 0, "PP-ERC20 : invalid data length" ); // NOTE: we assumed that nativeAsset's decimals is 18, if there is any nativeAsset with different decimals, need to change the 1e18 to the correct decimals uint256 tokenAmount = (requiredPreFund + (REFUND_POSTOP_COST) * userOp.maxFeePerGas) * priceMarkup * cachedPrice / (1e18 * priceDenominator); if (length == 32) { require( tokenAmount <= uint256(bytes32(userOp.paymasterAndData[20:52])), "PP-ERC20 : token amount too high" ); } SafeTransferLib.safeTransferFrom(address(token), userOp.sender, address(this), tokenAmount); context = abi.encodePacked(tokenAmount, userOp.sender); // No return here since validationData == 0 and we have context saved in memory validationResult = 0; } } /// @notice Performs post-operation tasks, such as updating the token price and refunding excess tokens. /// @dev This function is called after a user operation has been executed or reverted. /// @param mode The post-operation mode (either successful or reverted). /// @param context The context containing the token amount and user sender address. /// @param actualGasCost The actual gas cost of the transaction. function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override { if (mode == PostOpMode.postOpReverted) { return; // Do nothing here to not revert the whole bundle and harm reputation } unchecked { uint192 tokenPrice = fetchPrice(tokenOracle); uint192 nativeAsset = fetchPrice(nativeAssetOracle); uint256 cachedPrice = previousPrice; uint192 price = nativeAsset * uint192(tokenDecimals) / tokenPrice; uint256 cachedUpdateThreshold = priceUpdateThreshold; if ( uint256(price) * priceDenominator / cachedPrice > priceDenominator + cachedUpdateThreshold || uint256(price) * priceDenominator / cachedPrice < priceDenominator - cachedUpdateThreshold ) { previousPrice = uint192(int192(price)); cachedPrice = uint192(int192(price)); } // Refund tokens based on actual gas cost // NOTE: we assumed that nativeAsset's decimals is 18, if there is any nativeAsset with different decimals, need to change the 1e18 to the correct decimals uint256 actualTokenNeeded = (actualGasCost + REFUND_POSTOP_COST * tx.gasprice) * priceMarkup * cachedPrice / (1e18 * priceDenominator); // We use tx.gasprice here since we don't know the actual gas price used by the user if (uint256(bytes32(context[0:32])) > actualTokenNeeded) { // If the initially provided token amount is greater than the actual amount needed, refund the difference SafeTransferLib.safeTransfer( address(token), address(bytes20(context[32:52])), uint256(bytes32(context[0:32])) - actualTokenNeeded ); } // If the token amount is not greater than the actual amount needed, no refund occurs emit UserOperationSponsored(address(bytes20(context[32:52])), actualTokenNeeded, actualGasCost); } } /// @notice Fetches the latest price from the given Oracle. /// @dev This function is used to get the latest price from the tokenOracle or nativeAssetOracle. /// @param _oracle The Oracle contract to fetch the price from. /// @return price The latest price fetched from the Oracle. function fetchPrice(IOracle _oracle) internal view returns (uint192 price) { (uint80 roundId, int256 answer,, uint256 updatedAt, uint80 answeredInRound) = _oracle.latestRoundData(); require(answer > 0, "PP-ERC20 : Chainlink price <= 0"); // 2 days old price is considered stale since the price is updated every 24 hours require(updatedAt >= block.timestamp - 60 * 60 * 24 * 2, "PP-ERC20 : Incomplete round"); require(answeredInRound >= roundId, "PP-ERC20 : Stale price"); price = uint192(int192(answer)); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /* solhint-disable reason-string */ import "@openzeppelin/contracts/access/Ownable.sol"; import "../interfaces/IPaymaster.sol"; import "../interfaces/IEntryPoint.sol"; import "./Helpers.sol"; /** * Helper class for creating a paymaster. * provides helper methods for staking. * validates that the postOp is called only by the entryPoint */ abstract contract BasePaymaster is IPaymaster, Ownable { IEntryPoint immutable public entryPoint; constructor(IEntryPoint _entryPoint) { entryPoint = _entryPoint; } /// @inheritdoc IPaymaster function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) external override returns (bytes memory context, uint256 validationData) { _requireFromEntryPoint(); return _validatePaymasterUserOp(userOp, userOpHash, maxCost); } function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) internal virtual returns (bytes memory context, uint256 validationData); /// @inheritdoc IPaymaster function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external override { _requireFromEntryPoint(); _postOp(mode, context, actualGasCost); } /** * post-operation handler. * (verified to be called only through the entryPoint) * @dev if subclass returns a non-empty context from validatePaymasterUserOp, it must also implement this method. * @param mode enum with the following options: * opSucceeded - user operation succeeded. * opReverted - user op reverted. still has to pay for gas. * postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert. * Now this is the 2nd call, after user's op was deliberately reverted. * @param context - the context value returned by validatePaymasterUserOp * @param actualGasCost - actual gas used so far (without this postOp call). */ function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal virtual { (mode,context,actualGasCost); // unused params // subclass must override this method if validatePaymasterUserOp returns a context revert("must override"); } /** * add a deposit for this paymaster, used for paying for transaction fees */ function deposit() public payable { entryPoint.depositTo{value : msg.value}(address(this)); } /** * withdraw value from the deposit * @param withdrawAddress target to send to * @param amount to withdraw */ function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner { entryPoint.withdrawTo(withdrawAddress, amount); } /** * add stake for this paymaster. * This method can also carry eth value to add to the current stake. * @param unstakeDelaySec - the unstake delay for this paymaster. Can only be increased. */ function addStake(uint32 unstakeDelaySec) external payable onlyOwner { entryPoint.addStake{value : msg.value}(unstakeDelaySec); } /** * return current paymaster's deposit on the entryPoint. */ function getDeposit() public view returns (uint256) { return entryPoint.balanceOf(address(this)); } /** * unlock the stake, in order to withdraw it. * The paymaster can't serve requests once unlocked, until it calls addStake again */ function unlockStake() external onlyOwner { entryPoint.unlockStake(); } /** * withdraw the entire paymaster's stake. * stake must be unlocked first (and then wait for the unstakeDelay to be over) * @param withdrawAddress the address to send withdrawn value. */ function withdrawStake(address payable withdrawAddress) external onlyOwner { entryPoint.withdrawStake(withdrawAddress); } /// validate the call is made from a valid entrypoint function _requireFromEntryPoint() internal virtual { require(msg.sender == address(entryPoint), "Sender not EntryPoint"); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /* solhint-disable no-inline-assembly */ /** * returned data from validateUserOp. * validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData` * @param aggregator - address(0) - the account validated the signature by itself. * address(1) - the account failed to validate the signature. * otherwise - this is an address of a signature aggregator that must be used to validate the signature. * @param validAfter - this UserOp is valid only after this timestamp. * @param validaUntil - this UserOp is valid only up to this timestamp. */ struct ValidationData { address aggregator; uint48 validAfter; uint48 validUntil; } //extract sigFailed, validAfter, validUntil. // also convert zero validUntil to type(uint48).max function _parseValidationData(uint validationData) pure returns (ValidationData memory data) { address aggregator = address(uint160(validationData)); uint48 validUntil = uint48(validationData >> 160); if (validUntil == 0) { validUntil = type(uint48).max; } uint48 validAfter = uint48(validationData >> (48 + 160)); return ValidationData(aggregator, validAfter, validUntil); } // intersect account and paymaster ranges. function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) { ValidationData memory accountValidationData = _parseValidationData(validationData); ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData); address aggregator = accountValidationData.aggregator; if (aggregator == address(0)) { aggregator = pmValidationData.aggregator; } uint48 validAfter = accountValidationData.validAfter; uint48 validUntil = accountValidationData.validUntil; uint48 pmValidAfter = pmValidationData.validAfter; uint48 pmValidUntil = pmValidationData.validUntil; if (validAfter < pmValidAfter) validAfter = pmValidAfter; if (validUntil > pmValidUntil) validUntil = pmValidUntil; return ValidationData(aggregator, validAfter, validUntil); } /** * helper to pack the return value for validateUserOp * @param data - the ValidationData to pack */ function _packValidationData(ValidationData memory data) pure returns (uint256) { return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48)); } /** * helper to pack the return value for validateUserOp, when not using an aggregator * @param sigFailed - true for signature failure, false for success * @param validUntil last timestamp this UserOperation is valid (or zero for infinite) * @param validAfter first timestamp this UserOperation is valid */ function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) { return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48)); } /** * keccak function over calldata. * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it. */ function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) { assembly { let mem := mload(0x40) let len := data.length calldatacopy(mem, data.offset, len) ret := keccak256(mem, len) } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /* solhint-disable no-inline-assembly */ import {calldataKeccak} from "../core/Helpers.sol"; /** * User Operation struct * @param sender the sender account of this request. * @param nonce unique value the sender uses to verify it is not a replay. * @param initCode if set, the account contract will be created by this constructor/ * @param callData the method call to execute on this account. * @param callGasLimit the gas limit passed to the callData method call. * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp. * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead. * @param maxFeePerGas same as EIP-1559 gas parameter. * @param maxPriorityFeePerGas same as EIP-1559 gas parameter. * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender. * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID. */ struct UserOperation { address sender; uint256 nonce; bytes initCode; bytes callData; uint256 callGasLimit; uint256 verificationGasLimit; uint256 preVerificationGas; uint256 maxFeePerGas; uint256 maxPriorityFeePerGas; bytes paymasterAndData; bytes signature; } /** * Utility functions helpful when working with UserOperation structs. */ library UserOperationLib { function getSender(UserOperation calldata userOp) internal pure returns (address) { address data; //read sender from userOp, which is first userOp member (saves 800 gas...) assembly {data := calldataload(userOp)} return address(uint160(data)); } //relayer/block builder might submit the TX with higher priorityFee, but the user should not // pay above what he signed for. function gasPrice(UserOperation calldata userOp) internal view returns (uint256) { unchecked { uint256 maxFeePerGas = userOp.maxFeePerGas; uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; if (maxFeePerGas == maxPriorityFeePerGas) { //legacy mode (for networks that don't support basefee opcode) return maxFeePerGas; } return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); } } function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) { address sender = getSender(userOp); uint256 nonce = userOp.nonce; bytes32 hashInitCode = calldataKeccak(userOp.initCode); bytes32 hashCallData = calldataKeccak(userOp.callData); uint256 callGasLimit = userOp.callGasLimit; uint256 verificationGasLimit = userOp.verificationGasLimit; uint256 preVerificationGas = userOp.preVerificationGas; uint256 maxFeePerGas = userOp.maxFeePerGas; uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData); return abi.encode( sender, nonce, hashInitCode, hashCallData, callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas, hashPaymasterAndData ); } function hash(UserOperation calldata userOp) internal pure returns (bytes32) { return keccak256(pack(userOp)); } function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.0; import "./ECDSA.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding * they need in their contracts using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * _Available since v3.4._ */ abstract contract EIP712 { /* solhint-disable var-name-mixedcase */ // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to // invalidate the cached domain separator if the chain id changes. bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; address private immutable _CACHED_THIS; bytes32 private immutable _HASHED_NAME; bytes32 private immutable _HASHED_VERSION; bytes32 private immutable _TYPE_HASH; /* solhint-enable var-name-mixedcase */ /** * @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]. */ constructor(string memory name, string memory version) { bytes32 hashedName = keccak256(bytes(name)); bytes32 hashedVersion = keccak256(bytes(version)); bytes32 typeHash = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); _HASHED_NAME = hashedName; _HASHED_VERSION = hashedVersion; _CACHED_CHAIN_ID = block.chainid; _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); _CACHED_THIS = address(this); _TYPE_HASH = typeHash; } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { return _CACHED_DOMAIN_SEPARATOR; } else { return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); } } function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash ) private view returns (bytes32) { return keccak256(abi.encode(typeHash, nameHash, versionHash, 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 virtual returns (bytes32) { return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IOracle { function decimals() external view returns (uint8); function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); }
/** ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation. ** Only one instance required on each chain. **/ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /* solhint-disable avoid-low-level-calls */ /* solhint-disable no-inline-assembly */ import "../interfaces/IAccount.sol"; import "../interfaces/IPaymaster.sol"; import "../interfaces/IEntryPoint.sol"; import "../utils/Exec.sol"; import "./StakeManager.sol"; import "./SenderCreator.sol"; import "./Helpers.sol"; import "./NonceManager.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard { using UserOperationLib for UserOperation; SenderCreator private immutable senderCreator = new SenderCreator(); // internal value used during simulation: need to query aggregator. address private constant SIMULATE_FIND_AGGREGATOR = address(1); // marker for inner call revert on out of gas bytes32 private constant INNER_OUT_OF_GAS = hex'deaddead'; uint256 private constant REVERT_REASON_MAX_LEN = 2048; /** * for simulation purposes, validateUserOp (and validatePaymasterUserOp) must return this value * in case of signature failure, instead of revert. */ uint256 public constant SIG_VALIDATION_FAILED = 1; /** * compensate the caller's beneficiary address with the collected fees of all UserOperations. * @param beneficiary the address to receive the fees * @param amount amount to transfer. */ function _compensate(address payable beneficiary, uint256 amount) internal { require(beneficiary != address(0), "AA90 invalid beneficiary"); (bool success,) = beneficiary.call{value : amount}(""); require(success, "AA91 failed send to beneficiary"); } /** * execute a user op * @param opIndex index into the opInfo array * @param userOp the userOp to execute * @param opInfo the opInfo filled by validatePrepayment for this userOp. * @return collected the total amount this userOp paid. */ function _executeUserOp(uint256 opIndex, UserOperation calldata userOp, UserOpInfo memory opInfo) private returns (uint256 collected) { uint256 preGas = gasleft(); bytes memory context = getMemoryBytesFromOffset(opInfo.contextOffset); try this.innerHandleOp(userOp.callData, opInfo, context) returns (uint256 _actualGasCost) { collected = _actualGasCost; } catch { bytes32 innerRevertCode; assembly { returndatacopy(0, 0, 32) innerRevertCode := mload(0) } // handleOps was called with gas limit too low. abort entire bundle. if (innerRevertCode == INNER_OUT_OF_GAS) { //report paymaster, since if it is not deliberately caused by the bundler, // it must be a revert caused by paymaster. revert FailedOp(opIndex, "AA95 out of gas"); } uint256 actualGas = preGas - gasleft() + opInfo.preOpGas; collected = _handlePostOp(opIndex, IPaymaster.PostOpMode.postOpReverted, opInfo, context, actualGas); } } /** * Execute a batch of UserOperations. * no signature aggregator is used. * if any account requires an aggregator (that is, it returned an aggregator when * performing simulateValidation), then handleAggregatedOps() must be used instead. * @param ops the operations to execute * @param beneficiary the address to receive the fees */ function handleOps(UserOperation[] calldata ops, address payable beneficiary) public nonReentrant { uint256 opslen = ops.length; UserOpInfo[] memory opInfos = new UserOpInfo[](opslen); unchecked { for (uint256 i = 0; i < opslen; i++) { UserOpInfo memory opInfo = opInfos[i]; (uint256 validationData, uint256 pmValidationData) = _validatePrepayment(i, ops[i], opInfo); _validateAccountAndPaymasterValidationData(i, validationData, pmValidationData, address(0)); } uint256 collected = 0; emit BeforeExecution(); for (uint256 i = 0; i < opslen; i++) { collected += _executeUserOp(i, ops[i], opInfos[i]); } _compensate(beneficiary, collected); } //unchecked } /** * Execute a batch of UserOperation with Aggregators * @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts) * @param beneficiary the address to receive the fees */ function handleAggregatedOps( UserOpsPerAggregator[] calldata opsPerAggregator, address payable beneficiary ) public nonReentrant { uint256 opasLen = opsPerAggregator.length; uint256 totalOps = 0; for (uint256 i = 0; i < opasLen; i++) { UserOpsPerAggregator calldata opa = opsPerAggregator[i]; UserOperation[] calldata ops = opa.userOps; IAggregator aggregator = opa.aggregator; //address(1) is special marker of "signature error" require(address(aggregator) != address(1), "AA96 invalid aggregator"); if (address(aggregator) != address(0)) { // solhint-disable-next-line no-empty-blocks try aggregator.validateSignatures(ops, opa.signature) {} catch { revert SignatureValidationFailed(address(aggregator)); } } totalOps += ops.length; } UserOpInfo[] memory opInfos = new UserOpInfo[](totalOps); emit BeforeExecution(); uint256 opIndex = 0; for (uint256 a = 0; a < opasLen; a++) { UserOpsPerAggregator calldata opa = opsPerAggregator[a]; UserOperation[] calldata ops = opa.userOps; IAggregator aggregator = opa.aggregator; uint256 opslen = ops.length; for (uint256 i = 0; i < opslen; i++) { UserOpInfo memory opInfo = opInfos[opIndex]; (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(opIndex, ops[i], opInfo); _validateAccountAndPaymasterValidationData(i, validationData, paymasterValidationData, address(aggregator)); opIndex++; } } uint256 collected = 0; opIndex = 0; for (uint256 a = 0; a < opasLen; a++) { UserOpsPerAggregator calldata opa = opsPerAggregator[a]; emit SignatureAggregatorChanged(address(opa.aggregator)); UserOperation[] calldata ops = opa.userOps; uint256 opslen = ops.length; for (uint256 i = 0; i < opslen; i++) { collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]); opIndex++; } } emit SignatureAggregatorChanged(address(0)); _compensate(beneficiary, collected); } /// @inheritdoc IEntryPoint function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external override { UserOpInfo memory opInfo; _simulationOnlyValidations(op); (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(0, op, opInfo); ValidationData memory data = _intersectTimeRange(validationData, paymasterValidationData); numberMarker(); uint256 paid = _executeUserOp(0, op, opInfo); numberMarker(); bool targetSuccess; bytes memory targetResult; if (target != address(0)) { (targetSuccess, targetResult) = target.call(targetCallData); } revert ExecutionResult(opInfo.preOpGas, paid, data.validAfter, data.validUntil, targetSuccess, targetResult); } // A memory copy of UserOp static fields only. // Excluding: callData, initCode and signature. Replacing paymasterAndData with paymaster. struct MemoryUserOp { address sender; uint256 nonce; uint256 callGasLimit; uint256 verificationGasLimit; uint256 preVerificationGas; address paymaster; uint256 maxFeePerGas; uint256 maxPriorityFeePerGas; } struct UserOpInfo { MemoryUserOp mUserOp; bytes32 userOpHash; uint256 prefund; uint256 contextOffset; uint256 preOpGas; } /** * inner function to handle a UserOperation. * Must be declared "external" to open a call context, but it can only be called by handleOps. */ function innerHandleOp(bytes memory callData, UserOpInfo memory opInfo, bytes calldata context) external returns (uint256 actualGasCost) { uint256 preGas = gasleft(); require(msg.sender == address(this), "AA92 internal call only"); MemoryUserOp memory mUserOp = opInfo.mUserOp; uint callGasLimit = mUserOp.callGasLimit; unchecked { // handleOps was called with gas limit too low. abort entire bundle. if (gasleft() < callGasLimit + mUserOp.verificationGasLimit + 5000) { assembly { mstore(0, INNER_OUT_OF_GAS) revert(0, 32) } } } IPaymaster.PostOpMode mode = IPaymaster.PostOpMode.opSucceeded; if (callData.length > 0) { bool success = Exec.call(mUserOp.sender, 0, callData, callGasLimit); if (!success) { bytes memory result = Exec.getReturnData(REVERT_REASON_MAX_LEN); if (result.length > 0) { emit UserOperationRevertReason(opInfo.userOpHash, mUserOp.sender, mUserOp.nonce, result); } mode = IPaymaster.PostOpMode.opReverted; } } unchecked { uint256 actualGas = preGas - gasleft() + opInfo.preOpGas; //note: opIndex is ignored (relevant only if mode==postOpReverted, which is only possible outside of innerHandleOp) return _handlePostOp(0, mode, opInfo, context, actualGas); } } /** * generate a request Id - unique identifier for this request. * the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid. */ function getUserOpHash(UserOperation calldata userOp) public view returns (bytes32) { return keccak256(abi.encode(userOp.hash(), address(this), block.chainid)); } /** * copy general fields from userOp into the memory opInfo structure. */ function _copyUserOpToMemory(UserOperation calldata userOp, MemoryUserOp memory mUserOp) internal pure { mUserOp.sender = userOp.sender; mUserOp.nonce = userOp.nonce; mUserOp.callGasLimit = userOp.callGasLimit; mUserOp.verificationGasLimit = userOp.verificationGasLimit; mUserOp.preVerificationGas = userOp.preVerificationGas; mUserOp.maxFeePerGas = userOp.maxFeePerGas; mUserOp.maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; bytes calldata paymasterAndData = userOp.paymasterAndData; if (paymasterAndData.length > 0) { require(paymasterAndData.length >= 20, "AA93 invalid paymasterAndData"); mUserOp.paymaster = address(bytes20(paymasterAndData[: 20])); } else { mUserOp.paymaster = address(0); } } /** * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp. * @dev this method always revert. Successful result is ValidationResult error. other errors are failures. * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data. * @param userOp the user operation to validate. */ function simulateValidation(UserOperation calldata userOp) external { UserOpInfo memory outOpInfo; _simulationOnlyValidations(userOp); (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment(0, userOp, outOpInfo); StakeInfo memory paymasterInfo = _getStakeInfo(outOpInfo.mUserOp.paymaster); StakeInfo memory senderInfo = _getStakeInfo(outOpInfo.mUserOp.sender); StakeInfo memory factoryInfo; { bytes calldata initCode = userOp.initCode; address factory = initCode.length >= 20 ? address(bytes20(initCode[0 : 20])) : address(0); factoryInfo = _getStakeInfo(factory); } ValidationData memory data = _intersectTimeRange(validationData, paymasterValidationData); address aggregator = data.aggregator; bool sigFailed = aggregator == address(1); ReturnInfo memory returnInfo = ReturnInfo(outOpInfo.preOpGas, outOpInfo.prefund, sigFailed, data.validAfter, data.validUntil, getMemoryBytesFromOffset(outOpInfo.contextOffset)); if (aggregator != address(0) && aggregator != address(1)) { AggregatorStakeInfo memory aggregatorInfo = AggregatorStakeInfo(aggregator, _getStakeInfo(aggregator)); revert ValidationResultWithAggregation(returnInfo, senderInfo, factoryInfo, paymasterInfo, aggregatorInfo); } revert ValidationResult(returnInfo, senderInfo, factoryInfo, paymasterInfo); } function _getRequiredPrefund(MemoryUserOp memory mUserOp) internal pure returns (uint256 requiredPrefund) { unchecked { //when using a Paymaster, the verificationGasLimit is used also to as a limit for the postOp call. // our security model might call postOp eventually twice uint256 mul = mUserOp.paymaster != address(0) ? 3 : 1; uint256 requiredGas = mUserOp.callGasLimit + mUserOp.verificationGasLimit * mul + mUserOp.preVerificationGas; requiredPrefund = requiredGas * mUserOp.maxFeePerGas; } } // create the sender's contract if needed. function _createSenderIfNeeded(uint256 opIndex, UserOpInfo memory opInfo, bytes calldata initCode) internal { if (initCode.length != 0) { address sender = opInfo.mUserOp.sender; if (sender.code.length != 0) revert FailedOp(opIndex, "AA10 sender already constructed"); address sender1 = senderCreator.createSender{gas : opInfo.mUserOp.verificationGasLimit}(initCode); if (sender1 == address(0)) revert FailedOp(opIndex, "AA13 initCode failed or OOG"); if (sender1 != sender) revert FailedOp(opIndex, "AA14 initCode must return sender"); if (sender1.code.length == 0) revert FailedOp(opIndex, "AA15 initCode must create sender"); address factory = address(bytes20(initCode[0 : 20])); emit AccountDeployed(opInfo.userOpHash, sender, factory, opInfo.mUserOp.paymaster); } } /** * Get counterfactual sender address. * Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation. * this method always revert, and returns the address in SenderAddressResult error * @param initCode the constructor code to be passed into the UserOperation. */ function getSenderAddress(bytes calldata initCode) public { address sender = senderCreator.createSender(initCode); revert SenderAddressResult(sender); } function _simulationOnlyValidations(UserOperation calldata userOp) internal view { // solhint-disable-next-line no-empty-blocks try this._validateSenderAndPaymaster(userOp.initCode, userOp.sender, userOp.paymasterAndData) {} catch Error(string memory revertReason) { if (bytes(revertReason).length != 0) { revert FailedOp(0, revertReason); } } } /** * Called only during simulation. * This function always reverts to prevent warm/cold storage differentiation in simulation vs execution. */ function _validateSenderAndPaymaster(bytes calldata initCode, address sender, bytes calldata paymasterAndData) external view { if (initCode.length == 0 && sender.code.length == 0) { // it would revert anyway. but give a meaningful message revert("AA20 account not deployed"); } if (paymasterAndData.length >= 20) { address paymaster = address(bytes20(paymasterAndData[0 : 20])); if (paymaster.code.length == 0) { // it would revert anyway. but give a meaningful message revert("AA30 paymaster not deployed"); } } // always revert revert(""); } /** * call account.validateUserOp. * revert (with FailedOp) in case validateUserOp reverts, or account didn't send required prefund. * decrement account's deposit if needed */ function _validateAccountPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPrefund) internal returns (uint256 gasUsedByValidateAccountPrepayment, uint256 validationData) { unchecked { uint256 preGas = gasleft(); MemoryUserOp memory mUserOp = opInfo.mUserOp; address sender = mUserOp.sender; _createSenderIfNeeded(opIndex, opInfo, op.initCode); address paymaster = mUserOp.paymaster; numberMarker(); uint256 missingAccountFunds = 0; if (paymaster == address(0)) { uint256 bal = balanceOf(sender); missingAccountFunds = bal > requiredPrefund ? 0 : requiredPrefund - bal; } try IAccount(sender).validateUserOp{gas : mUserOp.verificationGasLimit}(op, opInfo.userOpHash, missingAccountFunds) returns (uint256 _validationData) { validationData = _validationData; } catch Error(string memory revertReason) { revert FailedOp(opIndex, string.concat("AA23 reverted: ", revertReason)); } catch { revert FailedOp(opIndex, "AA23 reverted (or OOG)"); } if (paymaster == address(0)) { DepositInfo storage senderInfo = deposits[sender]; uint256 deposit = senderInfo.deposit; if (requiredPrefund > deposit) { revert FailedOp(opIndex, "AA21 didn't pay prefund"); } senderInfo.deposit = uint112(deposit - requiredPrefund); } gasUsedByValidateAccountPrepayment = preGas - gasleft(); } } /** * In case the request has a paymaster: * Validate paymaster has enough deposit. * Call paymaster.validatePaymasterUserOp. * Revert with proper FailedOp in case paymaster reverts. * Decrement paymaster's deposit */ function _validatePaymasterPrepayment(uint256 opIndex, UserOperation calldata op, UserOpInfo memory opInfo, uint256 requiredPreFund, uint256 gasUsedByValidateAccountPrepayment) internal returns (bytes memory context, uint256 validationData) { unchecked { MemoryUserOp memory mUserOp = opInfo.mUserOp; uint256 verificationGasLimit = mUserOp.verificationGasLimit; require(verificationGasLimit > gasUsedByValidateAccountPrepayment, "AA41 too little verificationGas"); uint256 gas = verificationGasLimit - gasUsedByValidateAccountPrepayment; address paymaster = mUserOp.paymaster; DepositInfo storage paymasterInfo = deposits[paymaster]; uint256 deposit = paymasterInfo.deposit; if (deposit < requiredPreFund) { revert FailedOp(opIndex, "AA31 paymaster deposit too low"); } paymasterInfo.deposit = uint112(deposit - requiredPreFund); try IPaymaster(paymaster).validatePaymasterUserOp{gas : gas}(op, opInfo.userOpHash, requiredPreFund) returns (bytes memory _context, uint256 _validationData){ context = _context; validationData = _validationData; } catch Error(string memory revertReason) { revert FailedOp(opIndex, string.concat("AA33 reverted: ", revertReason)); } catch { revert FailedOp(opIndex, "AA33 reverted (or OOG)"); } } } /** * revert if either account validationData or paymaster validationData is expired */ function _validateAccountAndPaymasterValidationData(uint256 opIndex, uint256 validationData, uint256 paymasterValidationData, address expectedAggregator) internal view { (address aggregator, bool outOfTimeRange) = _getValidationData(validationData); if (expectedAggregator != aggregator) { revert FailedOp(opIndex, "AA24 signature error"); } if (outOfTimeRange) { revert FailedOp(opIndex, "AA22 expired or not due"); } //pmAggregator is not a real signature aggregator: we don't have logic to handle it as address. // non-zero address means that the paymaster fails due to some signature check (which is ok only during estimation) address pmAggregator; (pmAggregator, outOfTimeRange) = _getValidationData(paymasterValidationData); if (pmAggregator != address(0)) { revert FailedOp(opIndex, "AA34 signature error"); } if (outOfTimeRange) { revert FailedOp(opIndex, "AA32 paymaster expired or not due"); } } function _getValidationData(uint256 validationData) internal view returns (address aggregator, bool outOfTimeRange) { if (validationData == 0) { return (address(0), false); } ValidationData memory data = _parseValidationData(validationData); // solhint-disable-next-line not-rely-on-time outOfTimeRange = block.timestamp > data.validUntil || block.timestamp < data.validAfter; aggregator = data.aggregator; } /** * validate account and paymaster (if defined). * also make sure total validation doesn't exceed verificationGasLimit * this method is called off-chain (simulateValidation()) and on-chain (from handleOps) * @param opIndex the index of this userOp into the "opInfos" array * @param userOp the userOp to validate */ function _validatePrepayment(uint256 opIndex, UserOperation calldata userOp, UserOpInfo memory outOpInfo) private returns (uint256 validationData, uint256 paymasterValidationData) { uint256 preGas = gasleft(); MemoryUserOp memory mUserOp = outOpInfo.mUserOp; _copyUserOpToMemory(userOp, mUserOp); outOpInfo.userOpHash = getUserOpHash(userOp); // validate all numeric values in userOp are well below 128 bit, so they can safely be added // and multiplied without causing overflow uint256 maxGasValues = mUserOp.preVerificationGas | mUserOp.verificationGasLimit | mUserOp.callGasLimit | userOp.maxFeePerGas | userOp.maxPriorityFeePerGas; require(maxGasValues <= type(uint120).max, "AA94 gas values overflow"); uint256 gasUsedByValidateAccountPrepayment; (uint256 requiredPreFund) = _getRequiredPrefund(mUserOp); (gasUsedByValidateAccountPrepayment, validationData) = _validateAccountPrepayment(opIndex, userOp, outOpInfo, requiredPreFund); if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) { revert FailedOp(opIndex, "AA25 invalid account nonce"); } //a "marker" where account opcode validation is done and paymaster opcode validation is about to start // (used only by off-chain simulateValidation) numberMarker(); bytes memory context; if (mUserOp.paymaster != address(0)) { (context, paymasterValidationData) = _validatePaymasterPrepayment(opIndex, userOp, outOpInfo, requiredPreFund, gasUsedByValidateAccountPrepayment); } unchecked { uint256 gasUsed = preGas - gasleft(); if (userOp.verificationGasLimit < gasUsed) { revert FailedOp(opIndex, "AA40 over verificationGasLimit"); } outOpInfo.prefund = requiredPreFund; outOpInfo.contextOffset = getOffsetOfMemoryBytes(context); outOpInfo.preOpGas = preGas - gasleft() + userOp.preVerificationGas; } } /** * process post-operation. * called just after the callData is executed. * if a paymaster is defined and its validation returned a non-empty context, its postOp is called. * the excess amount is refunded to the account (or paymaster - if it was used in the request) * @param opIndex index in the batch * @param mode - whether is called from innerHandleOp, or outside (postOpReverted) * @param opInfo userOp fields and info collected during validation * @param context the context returned in validatePaymasterUserOp * @param actualGas the gas used so far by this user operation */ function _handlePostOp(uint256 opIndex, IPaymaster.PostOpMode mode, UserOpInfo memory opInfo, bytes memory context, uint256 actualGas) private returns (uint256 actualGasCost) { uint256 preGas = gasleft(); unchecked { address refundAddress; MemoryUserOp memory mUserOp = opInfo.mUserOp; uint256 gasPrice = getUserOpGasPrice(mUserOp); address paymaster = mUserOp.paymaster; if (paymaster == address(0)) { refundAddress = mUserOp.sender; } else { refundAddress = paymaster; if (context.length > 0) { actualGasCost = actualGas * gasPrice; if (mode != IPaymaster.PostOpMode.postOpReverted) { IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost); } else { // solhint-disable-next-line no-empty-blocks try IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(mode, context, actualGasCost) {} catch Error(string memory reason) { revert FailedOp(opIndex, string.concat("AA50 postOp reverted: ", reason)); } catch { revert FailedOp(opIndex, "AA50 postOp revert"); } } } } actualGas += preGas - gasleft(); actualGasCost = actualGas * gasPrice; if (opInfo.prefund < actualGasCost) { revert FailedOp(opIndex, "AA51 prefund below actualGasCost"); } uint256 refund = opInfo.prefund - actualGasCost; _incrementDeposit(refundAddress, refund); bool success = mode == IPaymaster.PostOpMode.opSucceeded; emit UserOperationEvent(opInfo.userOpHash, mUserOp.sender, mUserOp.paymaster, mUserOp.nonce, success, actualGasCost, actualGas); } // unchecked } /** * the gas price this UserOp agrees to pay. * relayer/block builder might submit the TX with higher priorityFee, but the user should not */ function getUserOpGasPrice(MemoryUserOp memory mUserOp) internal view returns (uint256) { unchecked { uint256 maxFeePerGas = mUserOp.maxFeePerGas; uint256 maxPriorityFeePerGas = mUserOp.maxPriorityFeePerGas; if (maxFeePerGas == maxPriorityFeePerGas) { //legacy mode (for networks that don't support basefee opcode) return maxFeePerGas; } return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); } } function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } function getOffsetOfMemoryBytes(bytes memory data) internal pure returns (uint256 offset) { assembly {offset := data} } function getMemoryBytesFromOffset(uint256 offset) internal pure returns (bytes memory data) { assembly {data := offset} } //place the NUMBER opcode in the code. // this is used as a marker during simulation, as this OP is completely banned from the simulated code of the // account and paymaster. function numberMarker() internal view { assembly {mstore(0, number())} } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH /// that disallows any storage writes. uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. /// Multiply by a small constant (e.g. 2), if needed. uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x0c, 0x23b872dd000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x00, 0x20) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. // Store the function selector of `balanceOf(address)`. mstore(0x0c, 0x70a08231000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x00, 0x20) } // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x00, 0x23b872dd) // The `amount` argument is already written to the memory word at 0x6c. amount := mload(0x60) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x00, 0x20) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. // Store the function selector of `transfer(address,uint256)`. mstore(0x00, 0xa9059cbb000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x00, 0x20) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x00, 0x20) } mstore(0x14, to) // Store the `to` argument. // The `amount` argument is already written to the memory word at 0x34. amount := mload(0x34) // Store the function selector of `transfer(address,uint256)`. mstore(0x00, 0xa9059cbb000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x00, 0x20) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. // Store the function selector of `approve(address,uint256)`. mstore(0x00, 0x095ea7b3000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `ApproveFailed()`. mstore(0x00, 0x3e3f8f73) // Revert with (offset, size). revert(0x00, 0x20) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. // Store the function selector of `balanceOf(address)`. mstore(0x00, 0x70a08231000000000000000000000000) amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./UserOperation.sol"; /** * the interface exposed by a paymaster contract, who agrees to pay the gas for user's operations. * a paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction. */ interface IPaymaster { enum PostOpMode { opSucceeded, // user op succeeded opReverted, // user op reverted. still has to pay for gas. postOpReverted //user op succeeded, but caused postOp to revert. Now it's a 2nd call, after user's op was deliberately reverted. } /** * payment validation: check if paymaster agrees to pay. * Must verify sender is the entryPoint. * Revert to reject this request. * Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted) * The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns. * @param userOp the user operation * @param userOpHash hash of the user's request data. * @param maxCost the maximum cost of this transaction (based on maximum gas and gas price from userOp) * @return context value to send to a postOp * zero length to signify postOp is not required. * @return validationData signature and time-range of this operation, encoded the same as the return value of validateUserOperation * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "authorizer" contract. * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" * <6-byte> validAfter - first timestamp this operation is valid * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) external returns (bytes memory context, uint256 validationData); /** * post-operation handler. * Must verify sender is the entryPoint * @param mode enum with the following options: * opSucceeded - user operation succeeded. * opReverted - user op reverted. still has to pay for gas. * postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert. * Now this is the 2nd call, after user's op was deliberately reverted. * @param context - the context value returned by validatePaymasterUserOp * @param actualGasCost - actual gas used so far (without this postOp call). */ function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external; }
/** ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation. ** Only one instance required on each chain. **/ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /* solhint-disable avoid-low-level-calls */ /* solhint-disable no-inline-assembly */ /* solhint-disable reason-string */ import "./UserOperation.sol"; import "./IStakeManager.sol"; import "./IAggregator.sol"; import "./INonceManager.sol"; interface IEntryPoint is IStakeManager, INonceManager { /*** * An event emitted after each successful request * @param userOpHash - unique identifier for the request (hash its entire content, except signature). * @param sender - the account that generates this request. * @param paymaster - if non-null, the paymaster that pays for this request. * @param nonce - the nonce value from the request. * @param success - true if the sender transaction succeeded, false if reverted. * @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation. * @param actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution). */ event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed); /** * account "sender" was deployed. * @param userOpHash the userOp that deployed this account. UserOperationEvent will follow. * @param sender the account that is deployed * @param factory the factory used to deploy this account (in the initCode) * @param paymaster the paymaster used by this UserOp */ event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster); /** * An event emitted if the UserOperation "callData" reverted with non-zero length * @param userOpHash the request unique identifier. * @param sender the sender of this request * @param nonce the nonce used in the request * @param revertReason - the return bytes from the (reverted) call to "callData". */ event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason); /** * an event emitted by handleOps(), before starting the execution loop. * any event emitted before this event, is part of the validation. */ event BeforeExecution(); /** * signature aggregator used by the following UserOperationEvents within this bundle. */ event SignatureAggregatorChanged(address indexed aggregator); /** * a custom revert error of handleOps, to identify the offending op. * NOTE: if simulateValidation passes successfully, there should be no reason for handleOps to fail on it. * @param opIndex - index into the array of ops to the failed one (in simulateValidation, this is always zero) * @param reason - revert reason * The string starts with a unique code "AAmn", where "m" is "1" for factory, "2" for account and "3" for paymaster issues, * so a failure can be attributed to the correct entity. * Should be caught in off-chain handleOps simulation and not happen on-chain. * Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts. */ error FailedOp(uint256 opIndex, string reason); /** * error case when a signature aggregator fails to verify the aggregated signature it had created. */ error SignatureValidationFailed(address aggregator); /** * Successful result from simulateValidation. * @param returnInfo gas and time-range returned values * @param senderInfo stake information about the sender * @param factoryInfo stake information about the factory (if any) * @param paymasterInfo stake information about the paymaster (if any) */ error ValidationResult(ReturnInfo returnInfo, StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo); /** * Successful result from simulateValidation, if the account returns a signature aggregator * @param returnInfo gas and time-range returned values * @param senderInfo stake information about the sender * @param factoryInfo stake information about the factory (if any) * @param paymasterInfo stake information about the paymaster (if any) * @param aggregatorInfo signature aggregation info (if the account requires signature aggregator) * bundler MUST use it to verify the signature, or reject the UserOperation */ error ValidationResultWithAggregation(ReturnInfo returnInfo, StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo, AggregatorStakeInfo aggregatorInfo); /** * return value of getSenderAddress */ error SenderAddressResult(address sender); /** * return value of simulateHandleOp */ error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult); //UserOps handled, per aggregator struct UserOpsPerAggregator { UserOperation[] userOps; // aggregator address IAggregator aggregator; // aggregated signature bytes signature; } /** * Execute a batch of UserOperation. * no signature aggregator is used. * if any account requires an aggregator (that is, it returned an aggregator when * performing simulateValidation), then handleAggregatedOps() must be used instead. * @param ops the operations to execute * @param beneficiary the address to receive the fees */ function handleOps(UserOperation[] calldata ops, address payable beneficiary) external; /** * Execute a batch of UserOperation with Aggregators * @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts) * @param beneficiary the address to receive the fees */ function handleAggregatedOps( UserOpsPerAggregator[] calldata opsPerAggregator, address payable beneficiary ) external; /** * generate a request Id - unique identifier for this request. * the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid. */ function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32); /** * Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp. * @dev this method always revert. Successful result is ValidationResult error. other errors are failures. * @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data. * @param userOp the user operation to validate. */ function simulateValidation(UserOperation calldata userOp) external; /** * gas and return values during simulation * @param preOpGas the gas used for validation (including preValidationGas) * @param prefund the required prefund for this operation * @param sigFailed validateUserOp's (or paymaster's) signature check failed * @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range) * @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range) * @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp) */ struct ReturnInfo { uint256 preOpGas; uint256 prefund; bool sigFailed; uint48 validAfter; uint48 validUntil; bytes paymasterContext; } /** * returned aggregated signature info. * the aggregator returned by the account, and its current stake. */ struct AggregatorStakeInfo { address aggregator; StakeInfo stakeInfo; } /** * Get counterfactual sender address. * Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation. * this method always revert, and returns the address in SenderAddressResult error * @param initCode the constructor code to be passed into the UserOperation. */ function getSenderAddress(bytes memory initCode) external; /** * simulate full execution of a UserOperation (including both validation and target execution) * this method will always revert with "ExecutionResult". * it performs full validation of the UserOperation, but ignores signature error. * an optional target address is called after the userop succeeds, and its value is returned * (before the entire call is reverted) * Note that in order to collect the the success/failure of the target call, it must be executed * with trace enabled to track the emitted events. * @param op the UserOperation to simulate * @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult * are set to the return from that call. * @param targetCallData callData to pass to target address */ function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { 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); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); 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. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./UserOperation.sol"; interface IAccount { /** * Validate user's signature and nonce * the entryPoint will make the call to the recipient only if this validation call returns successfully. * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). * This allows making a "simulation call" without a valid signature * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. * * @dev Must validate caller is the entryPoint. * Must validate the signature and nonce * @param userOp the operation that is about to be executed. * @param userOpHash hash of the user's request data. can be used as the basis for signature. * @param missingAccountFunds missing funds on the account's deposit in the entrypoint. * This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call. * The excess is left as a deposit in the entrypoint, for future calls. * can be withdrawn anytime using "entryPoint.withdrawTo()" * In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero. * @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "authorizer" contract. * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" * <6-byte> validAfter - first timestamp this operation is valid * If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure. * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) external returns (uint256 validationData); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity >=0.7.5 <0.9.0; // solhint-disable no-inline-assembly /** * Utility functions helpful when making different kinds of contract calls in Solidity. */ library Exec { function call( address to, uint256 value, bytes memory data, uint256 txGas ) internal returns (bool success) { assembly { success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0) } } function staticcall( address to, bytes memory data, uint256 txGas ) internal view returns (bool success) { assembly { success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0) } } function delegateCall( address to, bytes memory data, uint256 txGas ) internal returns (bool success) { assembly { success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0) } } // get returned data from last call or calldelegate function getReturnData(uint256 maxLen) internal pure returns (bytes memory returnData) { assembly { let len := returndatasize() if gt(len, maxLen) { len := maxLen } let ptr := mload(0x40) mstore(0x40, add(ptr, add(len, 0x20))) mstore(ptr, len) returndatacopy(add(ptr, 0x20), 0, len) returnData := ptr } } // revert with explicit byte array (probably reverted info from call) function revertWithData(bytes memory returnData) internal pure { assembly { revert(add(returnData, 32), mload(returnData)) } } function callAndRevert(address to, bytes memory data, uint256 maxLen) internal { bool success = call(to,0,data,gasleft()); if (!success) { revertWithData(getReturnData(maxLen)); } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.12; import "../interfaces/IStakeManager.sol"; /* solhint-disable avoid-low-level-calls */ /* solhint-disable not-rely-on-time */ /** * manage deposits and stakes. * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account) * stake is value locked for at least "unstakeDelay" by a paymaster. */ abstract contract StakeManager is IStakeManager { /// maps paymaster to their deposits and stakes mapping(address => DepositInfo) public deposits; /// @inheritdoc IStakeManager function getDepositInfo(address account) public view returns (DepositInfo memory info) { return deposits[account]; } // internal method to return just the stake info function _getStakeInfo(address addr) internal view returns (StakeInfo memory info) { DepositInfo storage depositInfo = deposits[addr]; info.stake = depositInfo.stake; info.unstakeDelaySec = depositInfo.unstakeDelaySec; } /// return the deposit (for gas payment) of the account function balanceOf(address account) public view returns (uint256) { return deposits[account].deposit; } receive() external payable { depositTo(msg.sender); } function _incrementDeposit(address account, uint256 amount) internal { DepositInfo storage info = deposits[account]; uint256 newAmount = info.deposit + amount; require(newAmount <= type(uint112).max, "deposit overflow"); info.deposit = uint112(newAmount); } /** * add to the deposit of the given account */ function depositTo(address account) public payable { _incrementDeposit(account, msg.value); DepositInfo storage info = deposits[account]; emit Deposited(account, info.deposit); } /** * add to the account's stake - amount and delay * any pending unstake is first cancelled. * @param unstakeDelaySec the new lock duration before the deposit can be withdrawn. */ function addStake(uint32 unstakeDelaySec) public payable { DepositInfo storage info = deposits[msg.sender]; require(unstakeDelaySec > 0, "must specify unstake delay"); require(unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time"); uint256 stake = info.stake + msg.value; require(stake > 0, "no stake specified"); require(stake <= type(uint112).max, "stake overflow"); deposits[msg.sender] = DepositInfo( info.deposit, true, uint112(stake), unstakeDelaySec, 0 ); emit StakeLocked(msg.sender, stake, unstakeDelaySec); } /** * attempt to unlock the stake. * the value can be withdrawn (using withdrawStake) after the unstake delay. */ function unlockStake() external { DepositInfo storage info = deposits[msg.sender]; require(info.unstakeDelaySec != 0, "not staked"); require(info.staked, "already unstaking"); uint48 withdrawTime = uint48(block.timestamp) + info.unstakeDelaySec; info.withdrawTime = withdrawTime; info.staked = false; emit StakeUnlocked(msg.sender, withdrawTime); } /** * withdraw from the (unlocked) stake. * must first call unlockStake and wait for the unstakeDelay to pass * @param withdrawAddress the address to send withdrawn value. */ function withdrawStake(address payable withdrawAddress) external { DepositInfo storage info = deposits[msg.sender]; uint256 stake = info.stake; require(stake > 0, "No stake to withdraw"); require(info.withdrawTime > 0, "must call unlockStake() first"); require(info.withdrawTime <= block.timestamp, "Stake withdrawal is not due"); info.unstakeDelaySec = 0; info.withdrawTime = 0; info.stake = 0; emit StakeWithdrawn(msg.sender, withdrawAddress, stake); (bool success,) = withdrawAddress.call{value : stake}(""); require(success, "failed to withdraw stake"); } /** * withdraw from the deposit. * @param withdrawAddress the address to send withdrawn value. * @param withdrawAmount the amount to withdraw. */ function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external { DepositInfo storage info = deposits[msg.sender]; require(withdrawAmount <= info.deposit, "Withdraw amount too large"); info.deposit = uint112(info.deposit - withdrawAmount); emit Withdrawn(msg.sender, withdrawAddress, withdrawAmount); (bool success,) = withdrawAddress.call{value : withdrawAmount}(""); require(success, "failed to withdraw"); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /** * helper contract for EntryPoint, to call userOp.initCode from a "neutral" address, * which is explicitly not the entryPoint itself. */ contract SenderCreator { /** * call the "initCode" factory to create and return the sender account address * @param initCode the initCode value from a UserOp. contains 20 bytes of factory address, followed by calldata * @return sender the returned address of the created account, or zero address on failure. */ function createSender(bytes calldata initCode) external returns (address sender) { address factory = address(bytes20(initCode[0 : 20])); bytes memory initCallData = initCode[20 :]; bool success; /* solhint-disable no-inline-assembly */ assembly { success := call(gas(), factory, 0, add(initCallData, 0x20), mload(initCallData), 0, 32) sender := mload(0) } if (!success) { sender = address(0); } } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "../interfaces/IEntryPoint.sol"; /** * nonce management functionality */ contract NonceManager is INonceManager { /** * The next valid sequence number for a given nonce key. */ mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber; function getNonce(address sender, uint192 key) public view override returns (uint256 nonce) { return nonceSequenceNumber[sender][key] | (uint256(key) << 64); } // allow an account to manually increment its own nonce. // (mainly so that during construction nonce can be made non-zero, // to "absorb" the gas cost of first nonce increment to 1st transaction (construction), // not to 2nd transaction) function incrementNonce(uint192 key) public override { nonceSequenceNumber[msg.sender][key]++; } /** * validate nonce uniqueness for this account. * called just after validateUserOp() */ function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) { uint192 key = uint192(nonce >> 64); uint64 seq = uint64(nonce); return nonceSequenceNumber[sender][key]++ == seq; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.12; /** * manage deposits and stakes. * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account) * stake is value locked for at least "unstakeDelay" by the staked entity. */ interface IStakeManager { event Deposited( address indexed account, uint256 totalDeposit ); event Withdrawn( address indexed account, address withdrawAddress, uint256 amount ); /// Emitted when stake or unstake delay are modified event StakeLocked( address indexed account, uint256 totalStaked, uint256 unstakeDelaySec ); /// Emitted once a stake is scheduled for withdrawal event StakeUnlocked( address indexed account, uint256 withdrawTime ); event StakeWithdrawn( address indexed account, address withdrawAddress, uint256 amount ); /** * @param deposit the entity's deposit * @param staked true if this entity is staked. * @param stake actual amount of ether staked for this entity. * @param unstakeDelaySec minimum delay to withdraw the stake. * @param withdrawTime - first block timestamp where 'withdrawStake' will be callable, or zero if already locked * @dev sizes were chosen so that (deposit,staked, stake) fit into one cell (used during handleOps) * and the rest fit into a 2nd cell. * 112 bit allows for 10^15 eth * 48 bit for full timestamp * 32 bit allows 150 years for unstake delay */ struct DepositInfo { uint112 deposit; bool staked; uint112 stake; uint32 unstakeDelaySec; uint48 withdrawTime; } //API struct used by getStakeInfo and simulateValidation struct StakeInfo { uint256 stake; uint256 unstakeDelaySec; } /// @return info - full deposit information of given account function getDepositInfo(address account) external view returns (DepositInfo memory info); /// @return the deposit (for gas payment) of the account function balanceOf(address account) external view returns (uint256); /** * add to the deposit of the given account */ function depositTo(address account) external payable; /** * add to the account's stake - amount and delay * any pending unstake is first cancelled. * @param _unstakeDelaySec the new lock duration before the deposit can be withdrawn. */ function addStake(uint32 _unstakeDelaySec) external payable; /** * attempt to unlock the stake. * the value can be withdrawn (using withdrawStake) after the unstake delay. */ function unlockStake() external; /** * withdraw from the (unlocked) stake. * must first call unlockStake and wait for the unstakeDelay to pass * @param withdrawAddress the address to send withdrawn value. */ function withdrawStake(address payable withdrawAddress) external; /** * withdraw from the deposit. * @param withdrawAddress the address to send withdrawn value. * @param withdrawAmount the amount to withdraw. */ function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./UserOperation.sol"; /** * Aggregated Signatures validator. */ interface IAggregator { /** * validate aggregated signature. * revert if the aggregated signature does not match the given list of operations. */ function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view; /** * validate signature of a single userOp * This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation * First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps. * @param userOp the userOperation received from the user. * @return sigForUserOp the value to put into the signature field of the userOp when calling handleOps. * (usually empty, unless account and aggregator support some kind of "multisig" */ function validateUserOpSignature(UserOperation calldata userOp) external view returns (bytes memory sigForUserOp); /** * aggregate multiple signatures into a single value. * This method is called off-chain to calculate the signature to pass with handleOps() * bundler MAY use optimized custom code perform this aggregation * @param userOps array of UserOperations to collect the signatures from. * @return aggregatedSignature the aggregated signature */ function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatedSignature); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; interface INonceManager { /** * Return the next nonce for this sender. * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop) * But UserOp with different keys can come with arbitrary order. * * @param sender the account address * @param key the high 192 bit of the nonce * @return nonce a full nonce to pass for next UserOp with this sender. */ function getNonce(address sender, uint192 key) external view returns (uint256 nonce); /** * Manually increment the nonce of the sender. * This method is exposed just for completeness.. * Account does NOT need to call it, neither during validation, nor elsewhere, * as the EntryPoint will update the nonce regardless. * Possible use-case is call it with various keys to "initialize" their nonces to one, so that future * UserOperations will not pay extra for the first transaction with a given key. */ function incrementNonce(uint192 key) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @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), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @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) { 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] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); 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); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @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 up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (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; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 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. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); 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 (rounding == Rounding.Up && 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 down. * * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * 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 + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * 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 10, 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 + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
{ "remappings": [ "@account-abstraction/=node_modules/@account-abstraction/", "@openzeppelin/=node_modules/@openzeppelin/", "account-abstraction/=lib/account-abstraction/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "hardhat/=node_modules/hardhat/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solady/=node_modules/solady/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "evmVersion": "paris", "viaIR": false, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"contract IERC20Metadata","name":"_token","type":"address"},{"internalType":"contract IEntryPoint","name":"_entryPoint","type":"address"},{"internalType":"contract IOracle","name":"_tokenOracle","type":"address"},{"internalType":"contract IOracle","name":"_nativeAssetOracle","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"priceMarkup","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"updateThreshold","type":"uint32"}],"name":"ConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"actualTokenNeeded","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actualGasCost","type":"uint256"}],"name":"UserOperationSponsored","type":"event"},{"inputs":[],"name":"REFUND_POSTOP_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"unstakeDelaySec","type":"uint32"}],"name":"addStake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"contract IEntryPoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nativeAssetOracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IPaymaster.PostOpMode","name":"mode","type":"uint8"},{"internalType":"bytes","name":"context","type":"bytes"},{"internalType":"uint256","name":"actualGasCost","type":"uint256"}],"name":"postOp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"previousPrice","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceDenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceMarkup","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceUpdateThreshold","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenOracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlockStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_priceMarkup","type":"uint32"},{"internalType":"uint32","name":"_updateThreshold","type":"uint32"}],"name":"updateConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updatePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"maxCost","type":"uint256"}],"name":"validatePaymasterUserOp","outputs":[{"internalType":"bytes","name":"context","type":"bytes"},{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"withdrawAddress","type":"address"}],"name":"withdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"withdrawAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101206040523480156200001257600080fd5b5060405162002329380380620023298339810160408190526200003591620003f0565b836200004133620002ad565b6001600160a01b0390811660805285811660a05283811660e052821661010052600180546001600160c01b031665030d4000864760c51b1790556200008681620002fd565b846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000c5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000eb919062000470565b620000f890600a620005b1565b60c08181525050826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000164919062000470565b60ff16600814620001cf5760405162461bcd60e51b815260206004820152602a60248201527f50502d4552433230203a20746f6b656e206f7261636c6520646563696d616c73604482015269040daeae6e840c4ca40760b31b60648201526084015b60405180910390fd5b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200020e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000234919062000470565b60ff16600814620002a25760405162461bcd60e51b815260206004820152603160248201527f50502d4552433230203a206e6174697665206173736574206f7261636c6520646044820152700cac6d2dac2d8e640daeae6e840c4ca407607b1b6064820152608401620001c6565b5050505050620005c2565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b620003076200037c565b6001600160a01b0381166200036e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401620001c6565b6200037981620002ad565b50565b6000546001600160a01b03163314620003d85760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620001c6565b565b6001600160a01b03811681146200037957600080fd5b600080600080600060a086880312156200040957600080fd5b85516200041681620003da565b60208701519095506200042981620003da565b60408701519094506200043c81620003da565b60608701519093506200044f81620003da565b60808701519092506200046281620003da565b809150509295509295909350565b6000602082840312156200048357600080fd5b815160ff811681146200049557600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620004f3578160001904821115620004d757620004d76200049c565b80851615620004e557918102915b93841c9390800290620004b7565b509250929050565b6000826200050c57506001620005ab565b816200051b57506000620005ab565b81600181146200053457600281146200053f576200055f565b6001915050620005ab565b60ff8411156200055357620005536200049c565b50506001821b620005ab565b5060208310610133831016604e8410600b841016171562000584575081810a620005ab565b620005908383620004b2565b8060001904821115620005a757620005a76200049c565b0290505b92915050565b60006200049560ff841683620004fb565b60805160a05160c05160e05161010051611cb262000677600039600081816104c40152818161093c01526111ae0152600081816102de0152818161090f015261118101526000818161022e0152818161096801526111fe01526000818161054601528181610a0501528181611380015261164f015260008181610427015281816105a40152818161066e01528181610a5301528181610b1a01528181610baa01528181610c5d01526110da0152611cb26000f3fe60806040526004361061018b5760003560e01c80639e281a98116100d6578063cdcf4b9b1161007f578063f2fde38b11610059578063f2fde38b146104e6578063f465c77e14610506578063fc0c546a1461053457600080fd5b8063cdcf4b9b14610493578063d0e30db0146104aa578063efb1ad5d146104b257600080fd5b8063bb9fe6bf116100b0578063bb9fe6bf14610449578063c23a5cea1461045e578063c399ec881461047e57600080fd5b80639e281a98146103d5578063a9a23409146103f5578063b0d691fe1461041557600080fd5b8063673a7e28116101385780638da5cb5b116101125780638da5cb5b1461033a578063914e245a146103655780639dbdb977146103bf57600080fd5b8063673a7e28146102b75780636c5ec25c146102cc578063715018a61461032557600080fd5b80633b97e856116101695780633b97e8561461021c5780633c2154bc1461025e5780633e04619d1461027e57600080fd5b80630396cb6014610190578063205c2878146101a55780633a34c83f146101c5575b600080fd5b6101a361019e366004611782565b610568565b005b3480156101b157600080fd5b506101a36101c03660046117c6565b61061a565b3480156101d157600080fd5b50600154610202907c0100000000000000000000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020015b60405180910390f35b34801561022857600080fd5b506102507f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610213565b34801561026a57600080fd5b506101a36102793660046117f2565b6106b2565b34801561028a57600080fd5b50600154610202907801000000000000000000000000000000000000000000000000900463ffffffff1681565b3480156102c357600080fd5b506101a3610908565b3480156102d857600080fd5b506103007f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610213565b34801561033157600080fd5b506101a36109e4565b34801561034657600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610300565b34801561037157600080fd5b506001546103969077ffffffffffffffffffffffffffffffffffffffffffffffff1681565b60405177ffffffffffffffffffffffffffffffffffffffffffffffff9091168152602001610213565b3480156103cb57600080fd5b50610250619c4081565b3480156103e157600080fd5b506101a36103f03660046117c6565b6109f8565b34801561040157600080fd5b506101a3610410366004611825565b610a2f565b34801561042157600080fd5b506103007f000000000000000000000000000000000000000000000000000000000000000081565b34801561045557600080fd5b506101a3610a49565b34801561046a57600080fd5b506101a36104793660046118b4565b610acd565b34801561048a57600080fd5b50610250610b79565b34801561049f57600080fd5b50610250620f424081565b6101a3610c2f565b3480156104be57600080fd5b506103007f000000000000000000000000000000000000000000000000000000000000000081565b3480156104f257600080fd5b506101a36105013660046118b4565b610cb7565b34801561051257600080fd5b506105266105213660046118d1565b610d6e565b604051610213929190611925565b34801561054057600080fd5b506103007f000000000000000000000000000000000000000000000000000000000000000081565b610570610d91565b6040517f0396cb6000000000000000000000000000000000000000000000000000000000815263ffffffff821660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690630396cb609034906024016000604051808303818588803b1580156105fe57600080fd5b505af1158015610612573d6000803e3d6000fd5b505050505050565b610622610d91565b6040517f205c287800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063205c287890604401600060405180830381600087803b1580156105fe57600080fd5b6106ba610d91565b62124f808263ffffffff161115610732576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f50502d4552433230203a207072696365206d61726b757020746f6f206869676860448201526064015b60405180910390fd5b620f42408263ffffffff1610156107a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f50502d4552433230203a207072696365206d61726b65757020746f6f206c6f776044820152606401610729565b620f42408163ffffffff16111561083d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f50502d4552433230203a20757064617465207468726573686f6c6420746f6f2060448201527f68696768000000000000000000000000000000000000000000000000000000006064820152608401610729565b6001805477ffffffffffffffffffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000063ffffffff8581169182027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16929092177c0100000000000000000000000000000000000000000000000000000000928516928302179092556040805192835260208301919091527ffed7660357162e9e060534e05beba94ac6e3bfb17b1f793bd7350aaed0e9e8c4910160405180910390a15050565b60006109337f0000000000000000000000000000000000000000000000000000000000000000610e12565b905060006109607f0000000000000000000000000000000000000000000000000000000000000000610e12565b90508161098d7f0000000000000000000000000000000000000000000000000000000000000000836119c7565b6109979190611a39565b600180547fffffffffffffffff0000000000000000000000000000000000000000000000001677ffffffffffffffffffffffffffffffffffffffffffffffff929092169190911790555050565b6109ec610d91565b6109f66000610ffe565b565b610a00610d91565b610a2b7f00000000000000000000000000000000000000000000000000000000000000008383611073565b5050565b610a376110c2565b610a4384848484611161565b50505050565b610a51610d91565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663bb9fe6bf6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ab957600080fd5b505af1158015610a43573d6000803e3d6000fd5b610ad5610d91565b6040517fc23a5cea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063c23a5cea90602401600060405180830381600087803b158015610b5e57600080fd5b505af1158015610b72573d6000803e3d6000fd5b5050505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190611a97565b905090565b6040517fb760faf90000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b760faf99034906024016000604051808303818588803b158015610b5e57600080fd5b610cbf610d91565b73ffffffffffffffffffffffffffffffffffffffff8116610d62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610729565b610d6b81610ffe565b50565b60606000610d7a6110c2565b610d85858585611440565b91509150935093915050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146109f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610729565b60008060008060008573ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e899190611aca565b94509450509350935060008313610efc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f50502d4552433230203a20436861696e6c696e6b207072696365203c3d2030006044820152606401610729565b610f096202a30042611b1a565b821015610f72576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f50502d4552433230203a20496e636f6d706c65746520726f756e6400000000006044820152606401610729565b8369ffffffffffffffffffff168169ffffffffffffffffffff161015610ff4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f50502d4552433230203a205374616c65207072696365000000000000000000006044820152606401610729565b5090949350505050565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d1560016000511417166110b8576390b8ec1860005260206000fd5b6000603452505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146109f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53656e646572206e6f7420456e747279506f696e7400000000000000000000006044820152606401610729565b600284600281111561117557611175611b33565b14610a435760006111a57f0000000000000000000000000000000000000000000000000000000000000000610e12565b905060006111d27f0000000000000000000000000000000000000000000000000000000000000000610e12565b60015490915077ffffffffffffffffffffffffffffffffffffffffffffffff90811690600090848116907f00000000000000000000000000000000000000000000000000000000000000008502168161122d5761122d611a0a565b60015491900491507c0100000000000000000000000000000000000000000000000000000000900463ffffffff16620f424080820190849077ffffffffffffffffffffffffffffffffffffffffffffffff8516028161128e5761128e611a0a565b0411806112cd575080620f42400383620f42408477ffffffffffffffffffffffffffffffffffffffffffffffff1602816112ca576112ca611a0a565b04105b1561131c57600180547fffffffffffffffff0000000000000000000000000000000000000000000000001677ffffffffffffffffffffffffffffffffffffffffffffffff841690811790915592505b60015460009069d3c21bcecceda1000000907801000000000000000000000000000000000000000000000000900463ffffffff163a619c400289010285020490508061136c602060008b8d611b62565b61137591611b8c565b11156113dd576113dd7f00000000000000000000000000000000000000000000000000000000000000006113ad603460208c8e611b62565b6113b691611bc8565b60601c838c8c6000906020926113ce93929190611b62565b6113d791611b8c565b03611073565b6113eb603460208a8c611b62565b6113f491611bc8565b60408051838152602081018a905260609290921c917f472a42a044527b87df02c0ce8e6c00c0057fac40d6c424c93c24b02322eb14b5910160405180910390a250505050505050505050565b60015460609060009077ffffffffffffffffffffffffffffffffffffffffffffffff168082036114cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f50502d4552433230203a207072696365206e6f742073657400000000000000006044820152606401610729565b600060146114de610120890189611c10565b9050039050807effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdf1660001461156e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f50502d4552433230203a20696e76616c69642064617461206c656e67746800006044820152606401610729565b60015460009069d3c21bcecceda1000000907801000000000000000000000000000000000000000000000000900463ffffffff1660e08a0135619c400288010284020490508160200361164a576115c9610120890189611c10565b6115d891603491601491611b62565b6115e191611b8c565b81111561164a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f50502d4552433230203a20746f6b656e20616d6f756e7420746f6f20686967686044820152606401610729565b6116827f000000000000000000000000000000000000000000000000000000000000000061167b60208b018b6118b4565b308461170c565b8061169060208a018a6118b4565b6040516020016116cf92919091825260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602082015260340190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905298600098509650505050505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c52602060006064601c6000895af13d15600160005114171661175b57637939f42460005260206000fd5b600060605260405250505050565b803563ffffffff8116811461177d57600080fd5b919050565b60006020828403121561179457600080fd5b61179d82611769565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610d6b57600080fd5b600080604083850312156117d957600080fd5b82356117e4816117a4565b946020939093013593505050565b6000806040838503121561180557600080fd5b61180e83611769565b915061181c60208401611769565b90509250929050565b6000806000806060858703121561183b57600080fd5b84356003811061184a57600080fd5b9350602085013567ffffffffffffffff8082111561186757600080fd5b818701915087601f83011261187b57600080fd5b81358181111561188a57600080fd5b88602082850101111561189c57600080fd5b95986020929092019750949560400135945092505050565b6000602082840312156118c657600080fd5b813561179d816117a4565b6000806000606084860312156118e657600080fd5b833567ffffffffffffffff8111156118fd57600080fd5b8401610160818703121561191057600080fd5b95602085013595506040909401359392505050565b604081526000835180604084015260005b818110156119535760208187018101516060868401015201611936565b5060006060828501015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150508260208301529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b77ffffffffffffffffffffffffffffffffffffffffffffffff828116828216818102831692918115828504821417611a0157611a01611998565b50505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff80841680611a8b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b600060208284031215611aa957600080fd5b5051919050565b805169ffffffffffffffffffff8116811461177d57600080fd5b600080600080600060a08688031215611ae257600080fd5b611aeb86611ab0565b9450602086015193506040860151925060608601519150611b0e60808701611ab0565b90509295509295909350565b81810381811115611b2d57611b2d611998565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60008085851115611b7257600080fd5b83861115611b7f57600080fd5b5050820193919092039150565b80356020831015611b2d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008135818116916014851015611c085780818660140360031b1b83161692505b505092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611c4557600080fd5b83018035915067ffffffffffffffff821115611c6057600080fd5b602001915036819003821315611c7557600080fd5b925092905056fea2646970667358221220377d6bf30f7c36090b54175e660cffaa71a8f454e2e5edb697a09f55cf1d22a964736f6c634300081300330000000000000000000000005fd84259d66cd46123540766be93dfe6d43130d70000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000006e44e50e3cc14dd16e01c590dc1d7020cb36ed4c00000000000000000000000061ec26aa57019c486b10502285c5a3d4a4750ad7000000000000000000000000433704c40f80cbff02e86fd36bc8bac5e31eb0c1
Deployed Bytecode
0x60806040526004361061018b5760003560e01c80639e281a98116100d6578063cdcf4b9b1161007f578063f2fde38b11610059578063f2fde38b146104e6578063f465c77e14610506578063fc0c546a1461053457600080fd5b8063cdcf4b9b14610493578063d0e30db0146104aa578063efb1ad5d146104b257600080fd5b8063bb9fe6bf116100b0578063bb9fe6bf14610449578063c23a5cea1461045e578063c399ec881461047e57600080fd5b80639e281a98146103d5578063a9a23409146103f5578063b0d691fe1461041557600080fd5b8063673a7e28116101385780638da5cb5b116101125780638da5cb5b1461033a578063914e245a146103655780639dbdb977146103bf57600080fd5b8063673a7e28146102b75780636c5ec25c146102cc578063715018a61461032557600080fd5b80633b97e856116101695780633b97e8561461021c5780633c2154bc1461025e5780633e04619d1461027e57600080fd5b80630396cb6014610190578063205c2878146101a55780633a34c83f146101c5575b600080fd5b6101a361019e366004611782565b610568565b005b3480156101b157600080fd5b506101a36101c03660046117c6565b61061a565b3480156101d157600080fd5b50600154610202907c0100000000000000000000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020015b60405180910390f35b34801561022857600080fd5b506102507f00000000000000000000000000000000000000000000000000000000000f424081565b604051908152602001610213565b34801561026a57600080fd5b506101a36102793660046117f2565b6106b2565b34801561028a57600080fd5b50600154610202907801000000000000000000000000000000000000000000000000900463ffffffff1681565b3480156102c357600080fd5b506101a3610908565b3480156102d857600080fd5b506103007f0000000000000000000000006e44e50e3cc14dd16e01c590dc1d7020cb36ed4c81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610213565b34801561033157600080fd5b506101a36109e4565b34801561034657600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610300565b34801561037157600080fd5b506001546103969077ffffffffffffffffffffffffffffffffffffffffffffffff1681565b60405177ffffffffffffffffffffffffffffffffffffffffffffffff9091168152602001610213565b3480156103cb57600080fd5b50610250619c4081565b3480156103e157600080fd5b506101a36103f03660046117c6565b6109f8565b34801561040157600080fd5b506101a3610410366004611825565b610a2f565b34801561042157600080fd5b506103007f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278981565b34801561045557600080fd5b506101a3610a49565b34801561046a57600080fd5b506101a36104793660046118b4565b610acd565b34801561048a57600080fd5b50610250610b79565b34801561049f57600080fd5b50610250620f424081565b6101a3610c2f565b3480156104be57600080fd5b506103007f00000000000000000000000061ec26aa57019c486b10502285c5a3d4a4750ad781565b3480156104f257600080fd5b506101a36105013660046118b4565b610cb7565b34801561051257600080fd5b506105266105213660046118d1565b610d6e565b604051610213929190611925565b34801561054057600080fd5b506103007f0000000000000000000000005fd84259d66cd46123540766be93dfe6d43130d781565b610570610d91565b6040517f0396cb6000000000000000000000000000000000000000000000000000000000815263ffffffff821660048201527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278973ffffffffffffffffffffffffffffffffffffffff1690630396cb609034906024016000604051808303818588803b1580156105fe57600080fd5b505af1158015610612573d6000803e3d6000fd5b505050505050565b610622610d91565b6040517f205c287800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789169063205c287890604401600060405180830381600087803b1580156105fe57600080fd5b6106ba610d91565b62124f808263ffffffff161115610732576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f50502d4552433230203a207072696365206d61726b757020746f6f206869676860448201526064015b60405180910390fd5b620f42408263ffffffff1610156107a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f50502d4552433230203a207072696365206d61726b65757020746f6f206c6f776044820152606401610729565b620f42408163ffffffff16111561083d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f50502d4552433230203a20757064617465207468726573686f6c6420746f6f2060448201527f68696768000000000000000000000000000000000000000000000000000000006064820152608401610729565b6001805477ffffffffffffffffffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000063ffffffff8581169182027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16929092177c0100000000000000000000000000000000000000000000000000000000928516928302179092556040805192835260208301919091527ffed7660357162e9e060534e05beba94ac6e3bfb17b1f793bd7350aaed0e9e8c4910160405180910390a15050565b60006109337f0000000000000000000000006e44e50e3cc14dd16e01c590dc1d7020cb36ed4c610e12565b905060006109607f00000000000000000000000061ec26aa57019c486b10502285c5a3d4a4750ad7610e12565b90508161098d7f00000000000000000000000000000000000000000000000000000000000f4240836119c7565b6109979190611a39565b600180547fffffffffffffffff0000000000000000000000000000000000000000000000001677ffffffffffffffffffffffffffffffffffffffffffffffff929092169190911790555050565b6109ec610d91565b6109f66000610ffe565b565b610a00610d91565b610a2b7f0000000000000000000000005fd84259d66cd46123540766be93dfe6d43130d78383611073565b5050565b610a376110c2565b610a4384848484611161565b50505050565b610a51610d91565b7f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278973ffffffffffffffffffffffffffffffffffffffff1663bb9fe6bf6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ab957600080fd5b505af1158015610a43573d6000803e3d6000fd5b610ad5610d91565b6040517fc23a5cea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789169063c23a5cea90602401600060405180830381600087803b158015610b5e57600080fd5b505af1158015610b72573d6000803e3d6000fd5b5050505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278973ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190611a97565b905090565b6040517fb760faf90000000000000000000000000000000000000000000000000000000081523060048201527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278973ffffffffffffffffffffffffffffffffffffffff169063b760faf99034906024016000604051808303818588803b158015610b5e57600080fd5b610cbf610d91565b73ffffffffffffffffffffffffffffffffffffffff8116610d62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610729565b610d6b81610ffe565b50565b60606000610d7a6110c2565b610d85858585611440565b91509150935093915050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146109f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610729565b60008060008060008573ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e899190611aca565b94509450509350935060008313610efc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f50502d4552433230203a20436861696e6c696e6b207072696365203c3d2030006044820152606401610729565b610f096202a30042611b1a565b821015610f72576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f50502d4552433230203a20496e636f6d706c65746520726f756e6400000000006044820152606401610729565b8369ffffffffffffffffffff168169ffffffffffffffffffff161015610ff4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f50502d4552433230203a205374616c65207072696365000000000000000000006044820152606401610729565b5090949350505050565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d1560016000511417166110b8576390b8ec1860005260206000fd5b6000603452505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278916146109f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53656e646572206e6f7420456e747279506f696e7400000000000000000000006044820152606401610729565b600284600281111561117557611175611b33565b14610a435760006111a57f0000000000000000000000006e44e50e3cc14dd16e01c590dc1d7020cb36ed4c610e12565b905060006111d27f00000000000000000000000061ec26aa57019c486b10502285c5a3d4a4750ad7610e12565b60015490915077ffffffffffffffffffffffffffffffffffffffffffffffff90811690600090848116907f00000000000000000000000000000000000000000000000000000000000f42408502168161122d5761122d611a0a565b60015491900491507c0100000000000000000000000000000000000000000000000000000000900463ffffffff16620f424080820190849077ffffffffffffffffffffffffffffffffffffffffffffffff8516028161128e5761128e611a0a565b0411806112cd575080620f42400383620f42408477ffffffffffffffffffffffffffffffffffffffffffffffff1602816112ca576112ca611a0a565b04105b1561131c57600180547fffffffffffffffff0000000000000000000000000000000000000000000000001677ffffffffffffffffffffffffffffffffffffffffffffffff841690811790915592505b60015460009069d3c21bcecceda1000000907801000000000000000000000000000000000000000000000000900463ffffffff163a619c400289010285020490508061136c602060008b8d611b62565b61137591611b8c565b11156113dd576113dd7f0000000000000000000000005fd84259d66cd46123540766be93dfe6d43130d76113ad603460208c8e611b62565b6113b691611bc8565b60601c838c8c6000906020926113ce93929190611b62565b6113d791611b8c565b03611073565b6113eb603460208a8c611b62565b6113f491611bc8565b60408051838152602081018a905260609290921c917f472a42a044527b87df02c0ce8e6c00c0057fac40d6c424c93c24b02322eb14b5910160405180910390a250505050505050505050565b60015460609060009077ffffffffffffffffffffffffffffffffffffffffffffffff168082036114cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f50502d4552433230203a207072696365206e6f742073657400000000000000006044820152606401610729565b600060146114de610120890189611c10565b9050039050807effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdf1660001461156e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f50502d4552433230203a20696e76616c69642064617461206c656e67746800006044820152606401610729565b60015460009069d3c21bcecceda1000000907801000000000000000000000000000000000000000000000000900463ffffffff1660e08a0135619c400288010284020490508160200361164a576115c9610120890189611c10565b6115d891603491601491611b62565b6115e191611b8c565b81111561164a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f50502d4552433230203a20746f6b656e20616d6f756e7420746f6f20686967686044820152606401610729565b6116827f0000000000000000000000005fd84259d66cd46123540766be93dfe6d43130d761167b60208b018b6118b4565b308461170c565b8061169060208a018a6118b4565b6040516020016116cf92919091825260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602082015260340190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905298600098509650505050505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c52602060006064601c6000895af13d15600160005114171661175b57637939f42460005260206000fd5b600060605260405250505050565b803563ffffffff8116811461177d57600080fd5b919050565b60006020828403121561179457600080fd5b61179d82611769565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610d6b57600080fd5b600080604083850312156117d957600080fd5b82356117e4816117a4565b946020939093013593505050565b6000806040838503121561180557600080fd5b61180e83611769565b915061181c60208401611769565b90509250929050565b6000806000806060858703121561183b57600080fd5b84356003811061184a57600080fd5b9350602085013567ffffffffffffffff8082111561186757600080fd5b818701915087601f83011261187b57600080fd5b81358181111561188a57600080fd5b88602082850101111561189c57600080fd5b95986020929092019750949560400135945092505050565b6000602082840312156118c657600080fd5b813561179d816117a4565b6000806000606084860312156118e657600080fd5b833567ffffffffffffffff8111156118fd57600080fd5b8401610160818703121561191057600080fd5b95602085013595506040909401359392505050565b604081526000835180604084015260005b818110156119535760208187018101516060868401015201611936565b5060006060828501015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150508260208301529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b77ffffffffffffffffffffffffffffffffffffffffffffffff828116828216818102831692918115828504821417611a0157611a01611998565b50505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff80841680611a8b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b600060208284031215611aa957600080fd5b5051919050565b805169ffffffffffffffffffff8116811461177d57600080fd5b600080600080600060a08688031215611ae257600080fd5b611aeb86611ab0565b9450602086015193506040860151925060608601519150611b0e60808701611ab0565b90509295509295909350565b81810381811115611b2d57611b2d611998565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60008085851115611b7257600080fd5b83861115611b7f57600080fd5b5050820193919092039150565b80356020831015611b2d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008135818116916014851015611c085780818660140360031b1b83161692505b505092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611c4557600080fd5b83018035915067ffffffffffffffff821115611c6057600080fd5b602001915036819003821315611c7557600080fd5b925092905056fea2646970667358221220377d6bf30f7c36090b54175e660cffaa71a8f454e2e5edb697a09f55cf1d22a964736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000005fd84259d66cd46123540766be93dfe6d43130d70000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000006e44e50e3cc14dd16e01c590dc1d7020cb36ed4c00000000000000000000000061ec26aa57019c486b10502285c5a3d4a4750ad7000000000000000000000000433704c40f80cbff02e86fd36bc8bac5e31eb0c1
-----Decoded View---------------
Arg [0] : _token (address): 0x5fd84259d66Cd46123540766Be93DFE6D43130D7
Arg [1] : _entryPoint (address): 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789
Arg [2] : _tokenOracle (address): 0x6e44e50E3cc14DD16e01C590DC1d7020cb36eD4C
Arg [3] : _nativeAssetOracle (address): 0x61Ec26aA57019C486B10502285c5A3D4A4750AD7
Arg [4] : _owner (address): 0x433704c40F80cBff02e86FD36Bc8baC5e31eB0c1
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000005fd84259d66cd46123540766be93dfe6d43130d7
Arg [1] : 0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789
Arg [2] : 0000000000000000000000006e44e50e3cc14dd16e01c590dc1d7020cb36ed4c
Arg [3] : 00000000000000000000000061ec26aa57019c486b10502285c5a3d4a4750ad7
Arg [4] : 000000000000000000000000433704c40f80cbff02e86fd36bc8bac5e31eb0c1
Deployed Bytecode Sourcemap
1032:9511:23:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3094:141:0;;;;;;:::i;:::-;;:::i;:::-;;2721:149;;;;;;;;;;-1:-1:-1;2721:149:0;;;;;:::i;:::-;;:::i;1941:34:23:-;;;;;;;;;;-1:-1:-1;1941:34:23;;;;;;;;;;;;;;1048:10:26;1036:23;;;1018:42;;1006:2;991:18;1941:34:23;;;;;;;;1508:38;;;;;;;;;;;;;;;;;;1217:25:26;;;1205:2;1190:18;1508:38:23;1071:177:26;3831:471:23;;;;;;;;;;-1:-1:-1;3831:471:23;;;;;:::i;:::-;;:::i;1839:25::-;;;;;;;;;;-1:-1:-1;1839:25:23;;;;;;;;;;;4758:305;;;;;;;;;;;;;:::i;1552:36::-;;;;;;;;;;;;;;;;;;1706:42:26;1694:55;;;1676:74;;1664:2;1649:18;1552:36:23;1514:242:26;1831:101:14;;;;;;;;;;;;;:::i;1201:85::-;;;;;;;;;;-1:-1:-1;1247:7:14;1273:6;;;1201:85;;1763:28:23;;;;;;;;;;-1:-1:-1;1763:28:23;;;;;;;;;;;2168:50:26;2156:63;;;2138:82;;2126:2;2111:18;1763:28:23;1992:234:26;1138:50:23;;;;;;;;;;;;1183:5;1138:50;;4523:143;;;;;;;;;;-1:-1:-1;4523:143:23;;;;;:::i;:::-;;:::i;1143:186:0:-;;;;;;;;;;-1:-1:-1;1143:186:0;;;;;:::i;:::-;;:::i;471:39::-;;;;;;;;;;;;;;;3588:83;;;;;;;;;;;;;:::i;3890:133::-;;;;;;;;;;-1:-1:-1;3890:133:0;;;;;:::i;:::-;;:::i;3318:111::-;;;;;;;;;;;;;:::i;1086:46:23:-;;;;;;;;;;;;1129:3;1086:46;;2474:105:0;;;:::i;1655:42:23:-;;;;;;;;;;;;;;;2081:198:14;;;;;;;;;;-1:-1:-1;2081:198:14;;;;;:::i;:::-;;:::i;632:290:0:-;;;;;;;;;;-1:-1:-1;632:290:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;1420:29:23:-;;;;;;;;;;;;;;;3094:141:0;1094:13:14;:11;:13::i;:::-;3173:55:0::1;::::0;;;;1048:10:26;1036:23;;3173:55:0::1;::::0;::::1;1018:42:26::0;3173:10:0::1;:19;;::::0;::::1;::::0;3201:9:::1;::::0;991:18:26;;3173:55:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;3094:141:::0;:::o;2721:149::-;1094:13:14;:11;:13::i;:::-;2817:46:0::1;::::0;;;;:21:::1;5815:55:26::0;;;2817:46:0::1;::::0;::::1;5797:74:26::0;5887:18;;;5880:34;;;2817:10:0::1;:21;::::0;::::1;::::0;5770:18:26;;2817:46:0::1;;;;;;;;;;;;;;;;;::::0;::::1;3831:471:23::0;1094:13:14;:11;:13::i;:::-;3952:5:23::1;3936:12;:21;;;;3928:66;;;::::0;::::1;::::0;;6127:2:26;3928:66:23::1;::::0;::::1;6109:21:26::0;;;6146:18;;;6139:30;6205:34;6185:18;;;6178:62;6257:18;;3928:66:23::1;;;;;;;;;4028:3;4012:12;:19;;;;4004:64;;;::::0;::::1;::::0;;6488:2:26;4004:64:23::1;::::0;::::1;6470:21:26::0;;;6507:18;;;6500:30;6566:34;6546:18;;;6539:62;6618:18;;4004:64:23::1;6286:356:26::0;4004:64:23::1;4106:3;4086:16;:23;;;;4078:72;;;::::0;::::1;::::0;;6849:2:26;4078:72:23::1;::::0;::::1;6831:21:26::0;6888:2;6868:18;;;6861:30;6927:34;6907:18;;;6900:62;6998:6;6978:18;;;6971:34;7022:19;;4078:72:23::1;6647:400:26::0;4078:72:23::1;4160:11;:26:::0;;4196:39;;4160:26;::::1;::::0;;::::1;::::0;;::::1;4196:39:::0;;;;;;;;;::::1;::::0;;::::1;;::::0;;;4250:45:::1;::::0;;7251:34:26;;;7316:2;7301:18;;7294:43;;;;4250:45:23::1;::::0;7195:18:26;4250:45:23::1;;;;;;;3831:471:::0;;:::o;4758:305::-;4866:18;4887:23;4898:11;4887:10;:23::i;:::-;4866:44;;4920:24;4947:29;4958:17;4947:10;:29::i;:::-;4920:56;-1:-1:-1;5046:10:23;5002:41;5029:13;4920:56;5002:41;:::i;:::-;:54;;;;:::i;:::-;4986:13;:70;;;;;;;;;;;;;;;-1:-1:-1;;4758:305:23:o;1831:101:14:-;1094:13;:11;:13::i;:::-;1895:30:::1;1922:1;1895:18;:30::i;:::-;1831:101::o:0;4523:143:23:-;1094:13:14;:11;:13::i;:::-;4603:56:23::1;4640:5;4648:2;4652:6;4603:28;:56::i;:::-;4523:143:::0;;:::o;1143:186:0:-;1251:24;:22;:24::i;:::-;1285:37;1293:4;1299:7;;1308:13;1285:7;:37::i;:::-;1143:186;;;;:::o;3588:83::-;1094:13:14;:11;:13::i;:::-;3640:10:0::1;:22;;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;3890:133:::0;1094:13:14;:11;:13::i;:::-;3975:41:0::1;::::0;;;;:24:::1;1694:55:26::0;;;3975:41:0::1;::::0;::::1;1676:74:26::0;3975:10:0::1;:24;::::0;::::1;::::0;1649:18:26;;3975:41:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;3890:133:::0;:::o;3318:111::-;3387:35;;;;;3416:4;3387:35;;;1676:74:26;3361:7:0;;3387:10;:20;;;;;1649:18:26;;3387:35:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3380:42;;3318:111;:::o;2474:105::-;2518:54;;;;;2566:4;2518:54;;;1676:74:26;2518:10:0;:20;;;;;2547:9;;1649:18:26;;2518:54:0;;;;;;;;;;;;;;;;;;;2081:198:14;1094:13;:11;:13::i;:::-;2169:22:::1;::::0;::::1;2161:73;;;::::0;::::1;::::0;;9076:2:26;2161:73:14::1;::::0;::::1;9058:21:26::0;9115:2;9095:18;;;9088:30;9154:34;9134:18;;;9127:62;9225:8;9205:18;;;9198:36;9251:19;;2161:73:14::1;8874:402:26::0;2161:73:14::1;2244:28;2263:8;2244:18;:28::i;:::-;2081:198:::0;:::o;632:290:0:-;764:20;786:22;821:24;:22;:24::i;:::-;862:53;887:6;895:10;907:7;862:24;:53::i;:::-;855:60;;;;632:290;;;;;;:::o;1359:130:14:-;1247:7;1273:6;1422:23;1273:6;719:10:18;1422:23:14;1414:68;;;;;;;9483:2:26;1414:68:14;;;9465:21:26;;;9502:18;;;9495:30;9561:34;9541:18;;;9534:62;9613:18;;1414:68:14;9281:356:26;9983:558:23;10043:13;10069:14;10085:13;10101:17;10120:22;10146:7;:23;;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10068:103;;;;;;;;;10198:1;10189:6;:10;10181:54;;;;;;;10506:2:26;10181:54:23;;;10488:21:26;10545:2;10525:18;;;10518:30;10584:33;10564:18;;;10557:61;10635:18;;10181:54:23;10304:355:26;10181:54:23;10356:34;10374:16;10356:15;:34;:::i;:::-;10343:9;:47;;10335:87;;;;;;;10999:2:26;10335:87:23;;;10981:21:26;11038:2;11018:18;;;11011:30;11077:29;11057:18;;;11050:57;11124:18;;10335:87:23;10797:351:26;10335:87:23;10459:7;10440:26;;:15;:26;;;;10432:61;;;;;;;11355:2:26;10432:61:23;;;11337:21:26;11394:2;11374:18;;;11367:30;11433:24;11413:18;;;11406:52;11475:18;;10432:61:23;11153:346:26;10432:61:23;-1:-1:-1;10526:6:23;;9983:558;-1:-1:-1;;;;9983:558:23:o;2433:187:14:-;2506:16;2525:6;;;2541:17;;;;;;;;;;2573:40;;2525:6;;;;;;;2573:40;;2506:16;2573:40;2496:124;2433:187;:::o;6074:1189:25:-;6237:2;6231:4;6224:16;6294:6;6288:4;6281:20;6434:34;6428:4;6421:48;6892:4;6886;6880;6874;6871:1;6864:5;6857;6852:45;6812:16;6805:24;6801:1;6794:4;6788:11;6785:18;6782:48;6510:405;6483:655;;7031:10;7025:4;7018:24;7119:4;7113;7106:18;6483:655;7245:1;7239:4;7232:15;6074:1189;;;:::o;4087:135:0:-;4156:10;:33;4178:10;4156:33;;4148:67;;;;;;;11706:2:26;4148:67:0;;;11688:21:26;11745:2;11725:18;;;11718:30;11784:23;11764:18;;;11757:51;11825:18;;4148:67:0;11504:345:26;7598:2081:23;7719:25;7711:4;:33;;;;;;;;:::i;:::-;7707:140;7760:7;7707:140;7880:18;7901:23;7912:11;7901:10;:23::i;:::-;7880:44;;7938:19;7960:29;7971:17;7960:10;:29::i;:::-;8025:13;;7938:51;;-1:-1:-1;8025:13:23;;;;;8003:19;;8068:49;;;;8090:13;8068:36;;:49;;;;;;:::i;:::-;8163:20;;8068:49;;;;-1:-1:-1;8163:20:23;;;;;1129:3;8268:40;;;;8254:11;;8218:14;;;:33;8254:11;8218:47;;;;:::i;:::-;;:90;:204;;;;8401:21;1129:3;8382:40;8368:11;1129:3;8340:5;8332:14;;:33;:47;;;;;:::i;:::-;;:90;8218:204;8197:365;;;8455:13;:38;;;;;;;;;;;;;;-1:-1:-1;8197:365:23;8878:11;;8797:25;;8923:23;;8878:11;;;;;8863;1183:5;8842:32;8826:48;;8825:64;:78;;:122;;-1:-1:-1;8825:122:23;9066:13;9076:2;9074:1;9066:7;;:13;:::i;:::-;9058:22;;;:::i;:::-;9050:51;9046:421;;;9243:209;9301:5;9345:14;9356:2;9353;9345:7;;:14;:::i;:::-;9337:23;;;:::i;:::-;9329:32;;9417:17;9399:7;;9407:1;9399:13;9409:2;9399:13;;;;;;;:::i;:::-;9391:22;;;:::i;:::-;9383:51;9243:28;:209::i;:::-;9611:14;9622:2;9619;9611:7;;:14;:::i;:::-;9603:23;;;:::i;:::-;9572:90;;;13250:25:26;;;13306:2;13291:18;;13284:34;;;9595:32:23;;;;;;9572:90;;13223:18:26;9572:90:23;;;;;;;7856:1817;;;;;;7598:2081;;;;:::o;5538:1620::-;5796:13;;5688:20;;5710:24;;5796:13;;5831:16;;;5823:53;;;;;;;13531:2:26;5823:53:23;;;13513:21:26;13570:2;13550:18;;;13543:30;13609:26;13589:18;;;13582:54;13653:18;;5823:53:23;13329:348:26;5823:53:23;5890:14;5940:2;5907:23;;;;:6;:23;:::i;:::-;:30;;:35;5890:52;;6152:6;6161:64;6152:73;6229:1;6152:78;6127:167;;;;;;;14469:2:26;6127:167:23;;;14451:21:26;14508:2;14488:18;;;14481:30;14547:32;14527:18;;;14520:60;14597:18;;6127:167:23;14267:354:26;6127:167:23;6563:11;;6476:19;;6608:23;;6563:11;;;;;6540:19;;;;1183:5;6517:42;6499:60;;6498:76;:106;;:134;6476:156;;6650:6;6660:2;6650:12;6646:197;;6742:23;;;;:6;:23;:::i;:::-;:30;;6769:2;;6766;;6742:30;:::i;:::-;6734:39;;;:::i;:::-;6711:63;;;6682:146;;;;;;;14828:2:26;6682:146:23;;;14810:21:26;;;14847:18;;;14840:30;14906:34;14886:18;;;14879:62;14958:18;;6682:146:23;14626:356:26;6682:146:23;6856:91;6897:5;6905:13;;;;:6;:13;:::i;:::-;6928:4;6935:11;6856:32;:91::i;:::-;6988:11;7001:13;;;;:6;:13;:::i;:::-;6971:44;;;;;;;;15144:19:26;;;15201:2;15197:15;15214:66;15193:88;15188:2;15179:12;;15172:110;15307:2;15298:12;;14987:329;6971:44:23;;;;;;;;;;;;;;;7140:1;;-1:-1:-1;5538:1620:23;-1:-1:-1;;;;;;;5538:1620:23:o;2319:1379:25:-;2502:4;2496:11;2568:6;2562:4;2555:20;2633:2;2627:4;2620:16;2698:4;2694:2;2690:13;2684:4;2677:27;2847:34;2841:4;2834:48;3305:4;3299;3293;3287;3284:1;3277:5;3270;3265:45;3225:16;3218:24;3214:1;3207:4;3201:11;3198:18;3195:48;2923:405;2896:659;;3448:10;3442:4;3435:24;3536:4;3530;3523:18;2896:659;3582:1;3576:4;3569:15;3638:4;3631:15;-1:-1:-1;;;;2319:1379:25:o;14:163:26:-;81:20;;141:10;130:22;;120:33;;110:61;;167:1;164;157:12;110:61;14:163;;;:::o;182:184::-;240:6;293:2;281:9;272:7;268:23;264:32;261:52;;;309:1;306;299:12;261:52;332:28;350:9;332:28;:::i;:::-;322:38;182:184;-1:-1:-1;;;182:184:26:o;371:162::-;465:42;458:5;454:54;447:5;444:65;434:93;;523:1;520;513:12;538:331;614:6;622;675:2;663:9;654:7;650:23;646:32;643:52;;;691:1;688;681:12;643:52;730:9;717:23;749:39;782:5;749:39;:::i;:::-;807:5;859:2;844:18;;;;831:32;;-1:-1:-1;;;538:331:26:o;1253:256::-;1319:6;1327;1380:2;1368:9;1359:7;1355:23;1351:32;1348:52;;;1396:1;1393;1386:12;1348:52;1419:28;1437:9;1419:28;:::i;:::-;1409:38;;1466:37;1499:2;1488:9;1484:18;1466:37;:::i;:::-;1456:47;;1253:256;;;;;:::o;2559:818::-;2662:6;2670;2678;2686;2739:2;2727:9;2718:7;2714:23;2710:32;2707:52;;;2755:1;2752;2745:12;2707:52;2794:9;2781:23;2833:1;2826:5;2823:12;2813:40;;2849:1;2846;2839:12;2813:40;2872:5;-1:-1:-1;2928:2:26;2913:18;;2900:32;2951:18;2981:14;;;2978:34;;;3008:1;3005;2998:12;2978:34;3046:6;3035:9;3031:22;3021:32;;3091:7;3084:4;3080:2;3076:13;3072:27;3062:55;;3113:1;3110;3103:12;3062:55;3153:2;3140:16;3179:2;3171:6;3168:14;3165:34;;;3195:1;3192;3185:12;3165:34;3240:7;3235:2;3226:6;3222:2;3218:15;3214:24;3211:37;3208:57;;;3261:1;3258;3251:12;3208:57;2559:818;;3292:2;3284:11;;;;;-1:-1:-1;3314:6:26;;3367:2;3352:18;3339:32;;-1:-1:-1;2559:818:26;-1:-1:-1;;;2559:818:26:o;3633:263::-;3700:6;3753:2;3741:9;3732:7;3728:23;3724:32;3721:52;;;3769:1;3766;3759:12;3721:52;3808:9;3795:23;3827:39;3860:5;3827:39;:::i;4161:529::-;4271:6;4279;4287;4340:2;4328:9;4319:7;4315:23;4311:32;4308:52;;;4356:1;4353;4346:12;4308:52;4396:9;4383:23;4429:18;4421:6;4418:30;4415:50;;;4461:1;4458;4451:12;4415:50;4484:22;;4540:3;4522:16;;;4518:26;4515:46;;;4557:1;4554;4547:12;4515:46;4580:2;4629;4614:18;;4601:32;;-1:-1:-1;4680:2:26;4665:18;;;4652:32;;4161:529;-1:-1:-1;;;4161:529:26:o;4695:661::-;4870:2;4859:9;4852:21;4833:4;4902:6;4896:13;4945:6;4940:2;4929:9;4925:18;4918:34;4970:1;4980:144;4994:6;4991:1;4988:13;4980:144;;;5107:4;5091:14;;;5087:25;;5081:32;5076:2;5057:17;;;5053:26;5046:68;5009:12;4980:144;;;4984:3;5173:1;5168:2;5159:6;5148:9;5144:22;5140:31;5133:42;5302:2;5232:66;5227:2;5219:6;5215:15;5211:88;5200:9;5196:104;5192:113;5184:121;;;5343:6;5336:4;5325:9;5321:20;5314:36;4695:661;;;;;:::o;7348:184::-;7400:77;7397:1;7390:88;7497:4;7494:1;7487:15;7521:4;7518:1;7511:15;7537:316;7609:50;7709:10;;;7679;;;7743:13;;;7739:22;;;7609:50;7783:11;;7804:17;;;7796:26;;7780:43;7770:77;;7827:18;;:::i;:::-;7770:77;;;7537:316;;;;:::o;7858:184::-;7910:77;7907:1;7900:88;8007:4;8004:1;7997:15;8031:4;8028:1;8021:15;8047:386;8087:1;8113:50;8190:2;8187:1;8183:10;8212:3;8202:191;;8249:77;8246:1;8239:88;8350:4;8347:1;8340:15;8378:4;8375:1;8368:15;8202:191;8411:10;;8407:20;;;;;8047:386;-1:-1:-1;;8047:386:26:o;8685:184::-;8755:6;8808:2;8796:9;8787:7;8783:23;8779:32;8776:52;;;8824:1;8821;8814:12;8776:52;-1:-1:-1;8847:16:26;;8685:184;-1:-1:-1;8685:184:26:o;9642:179::-;9720:13;;9773:22;9762:34;;9752:45;;9742:73;;9811:1;9808;9801:12;9826:473;9929:6;9937;9945;9953;9961;10014:3;10002:9;9993:7;9989:23;9985:33;9982:53;;;10031:1;10028;10021:12;9982:53;10054:39;10083:9;10054:39;:::i;:::-;10044:49;;10133:2;10122:9;10118:18;10112:25;10102:35;;10177:2;10166:9;10162:18;10156:25;10146:35;;10221:2;10210:9;10206:18;10200:25;10190:35;;10244:49;10288:3;10277:9;10273:19;10244:49;:::i;:::-;10234:59;;9826:473;;;;;;;;:::o;10664:128::-;10731:9;;;10752:11;;;10749:37;;;10766:18;;:::i;:::-;10664:128;;;;:::o;11854:184::-;11906:77;11903:1;11896:88;12003:4;12000:1;11993:15;12027:4;12024:1;12017:15;12043:331;12148:9;12159;12201:8;12189:10;12186:24;12183:44;;;12223:1;12220;12213:12;12183:44;12252:6;12242:8;12239:20;12236:40;;;12272:1;12269;12262:12;12236:40;-1:-1:-1;;12298:23:26;;;12343:25;;;;;-1:-1:-1;12043:331:26:o;12379:315::-;12499:19;;12538:2;12530:11;;12527:161;;;12610:66;12599:2;12595:12;;;12592:1;12588:20;12584:93;12573:105;12379:315;;;;:::o;12699:372::-;12858:66;12820:19;;12942:11;;;;12973:2;12965:11;;12962:103;;;13052:2;13046;13039:3;13035:2;13031:12;13028:1;13024:20;13020:29;13016:2;13012:38;13008:47;12999:56;;12962:103;;;12699:372;;;;:::o;13682:580::-;13759:4;13765:6;13825:11;13812:25;13915:66;13904:8;13888:14;13884:29;13880:102;13860:18;13856:127;13846:155;;13997:1;13994;13987:12;13846:155;14024:33;;14076:20;;;-1:-1:-1;14119:18:26;14108:30;;14105:50;;;14151:1;14148;14141:12;14105:50;14184:4;14172:17;;-1:-1:-1;14215:14:26;14211:27;;;14201:38;;14198:58;;;14252:1;14249;14242:12;14198:58;13682:580;;;;;:::o
Swarm Source
ipfs://377d6bf30f7c36090b54175e660cffaa71a8f454e2e5edb697a09f55cf1d22a9
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.