Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 18647273 | 40 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
NodePaymasterFactory
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 999 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import { NodePaymaster } from "./NodePaymaster.sol";
import { IEntryPoint } from "account-abstraction/interfaces/IEntryPoint.sol";
/// @title NodePaymasterFactory
/// @notice A factory for deploying and funding NodePaymasters
/// @dev The NodePaymaster is deployed using create2 with a deterministic address
/// @dev The NodePaymaster is funded with the msg.value
/// @author Biconomy
contract NodePaymasterFactory {
/// @notice The error thrown when the NodePaymaster deployment fails
error NodePMDeployFailed();
/// @notice Deploy and fund a new NodePaymaster
/// @param entryPoint The 4337 EntryPoint address expected to call the NodePaymaster
/// @param owner The owner of the NodePaymaster
/// @param workerEoas The worker EOAs of a given node. Only those will be allowed
/// to call handleOps
/// @param index The deployment index of the NodePaymaster
/// @return nodePaymaster The address of the deployed NodePaymaster
/// @dev The NodePaymaster is deployed using create2 with a deterministic address
/// @dev The NodePaymaster is funded with the msg.value
function deployAndFundNodePaymaster(
address entryPoint,
address owner,
address[] calldata workerEoas,
uint256 index
)
public
payable
returns (address nodePaymaster)
{
address expectedPm = _predictNodePaymasterAddress(entryPoint, owner, workerEoas, index);
bytes memory deploymentData =
abi.encodePacked(type(NodePaymaster).creationCode, abi.encode(entryPoint, owner, workerEoas));
assembly {
nodePaymaster := create2(0x0, add(0x20, deploymentData), mload(deploymentData), index)
}
if (address(nodePaymaster) == address(0) || address(nodePaymaster) != expectedPm) {
revert NodePMDeployFailed();
}
// deposit the msg.value to the EP at the node paymaster's name
IEntryPoint(entryPoint).depositTo{ value: msg.value }(nodePaymaster);
}
/// @notice Get the counterfactual address of a NodePaymaster
/// @param entryPoint The 4337 EntryPoint address expected to call the NodePaymaster
/// @param owner The owner of the NodePaymaster
/// @param workerEoas The worker EOAs of a given node. Only those will be allowed
/// to call handleOps
/// @param index The deployment index of the NodePaymaster
/// @return nodePmAddress The counterfactual address of the NodePaymaster
function getNodePaymasterAddress(
address entryPoint,
address owner,
address[] calldata workerEoas,
uint256 index
)
public
view
returns (address nodePmAddress)
{
nodePmAddress = _predictNodePaymasterAddress(entryPoint, owner, workerEoas, index);
}
/// @notice Function to check if some EOA got PmContract deployed
/// @param entryPoint The 4337 EntryPoint address expected to call the NodePaymaster
/// @param owner The owner of the NodePaymaster
/// @param workerEoas The worker EOAs of a given node. Only those will be allowed
/// to call handleOps
/// @param index The deployment index of the NodePaymaster
/// @return nodePmAddress The predicted address of the NodePaymaster
function _predictNodePaymasterAddress(
address entryPoint,
address owner,
address[] calldata workerEoas,
uint256 index
)
internal
view
returns (address nodePmAddress)
{
/// forge-lint:disable-start(asm-keccak256)
bytes32 initCodeHash =
keccak256(abi.encodePacked(type(NodePaymaster).creationCode, abi.encode(entryPoint, owner, workerEoas)));
/// forge-lint:disable-end(asm-keccak256)
// Return the predicted address
uint256 predictedAddress;
// keccak256(abi.encodePacked(bytes1(0xff), address(this), index, initCodeHash))
assembly {
let ptr := mload(0x40)
mstore8(ptr, 0xff)
mstore(add(ptr, 0x01), shl(96, address()))
mstore(add(ptr, 0x15), index)
mstore(add(ptr, 0x35), initCodeHash)
predictedAddress := keccak256(ptr, 0x55)
// restore free memory ptr
mstore(0x40, add(ptr, 0x55))
}
/// forge-lint:disable-next-line(unsafe-typecast)
return payable(address(uint160(predictedAddress)));
}
/// @notice Returns the version of the NodePaymasterFactory
/// @return _version version of the NodePaymasterFactory
/// @dev adds versioning to the NodePaymasterFactory
function version() external pure returns (string memory _version) {
_version = "1.0.1";
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import { IEntryPoint } from "account-abstraction/interfaces/IEntryPoint.sol";
import { PackedUserOperation } from "account-abstraction/core/UserOperationLib.sol";
import { BaseNodePaymaster } from "./BaseNodePaymaster.sol";
/**
* @title Node Paymaster
* @notice A paymaster every MEE Node should deploy.
* @dev Allows handleOps calls by any address allowed by owner().
* It is used to sponsor userOps. Introduced for gas efficient MEE flow.
*/
contract NodePaymaster is BaseNodePaymaster {
mapping(address workerEOA => bool isWhitelisted) private _workerEOAs;
constructor(
IEntryPoint _entryPoint,
address _meeNodeMasterEOA,
address[] memory workerEOAs
)
payable
BaseNodePaymaster(_entryPoint, _meeNodeMasterEOA)
{
uint256 length = workerEOAs.length;
for (uint256 i; i < length; ++i) {
_workerEOAs[workerEOAs[i]] = true;
}
}
/**
* @dev Accepts all userOps
* Verifies that the handleOps is called by the MEE Node, so it sponsors only for superTxns by owner MEE Node
* @dev The use of tx.origin makes the NodePaymaster incompatible with the general ERC4337 mempool.
* This is intentional, and the NodePaymaster is restricted to the MEE node owner anyway.
*
* PaymasterAndData is encoded as follows:
* 20 bytes: Paymaster address
* 32 bytes: pm gas values
* 4 bytes: mode
* 4 bytes: premium mode
* 24 bytes: financial data:: premiumPercentage or fixedPremium
* 20 bytes: refundReceiver (only for DAPP mode)
*
* @param userOp the userOp to validate
* @param userOpHash the hash of the userOp
* @param maxCost the max cost of the userOp
* @return context the context to be used in the postOp
* @return validationData the validationData to be used in the postOp
*/
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
)
internal
virtual
override
returns (bytes memory context, uint256 validationData)
{
// solhint-disable-next-line avoid-tx-origin
if (tx.origin == owner() || _workerEOAs[tx.origin]) {
(context, validationData) = _validate(userOp, userOpHash, maxCost);
} else {
validationData = 1;
}
}
// ====== Manage worker EOAs ======
/**
* @notice Whitelist a worker EOA
* @param workerEOA The worker EOA to whitelist
*/
function whitelistWorkerEOA(address workerEOA) external onlyOwner {
_workerEOAs[workerEOA] = true;
}
/**
* @notice Whitelist a list of worker EOAs
* @param workerEOAs The list of worker EOAs to whitelist
*/
function whitelistWorkerEOAs(address[] calldata workerEOAs) external onlyOwner {
uint256 length = workerEOAs.length;
for (uint256 i; i < length; ++i) {
_workerEOAs[workerEOAs[i]] = true;
}
}
/**
* @notice Remove a worker EOA from the whitelist
* @param workerEOA The worker EOA to remove from the whitelist
*/
function removeWorkerEOAFromWhitelist(address workerEOA) external onlyOwner {
_workerEOAs[workerEOA] = false;
}
/**
* @notice Remove a list of worker EOAs from the whitelist
* @param workerEOAs The list of worker EOAs to remove from the whitelist
*/
function removeWorkerEOAsFromWhitelist(address[] calldata workerEOAs) external onlyOwner {
uint256 length = workerEOAs.length;
for (uint256 i; i < length; ++i) {
_workerEOAs[workerEOAs[i]] = false;
}
}
/**
* @notice Check if a worker EOA is whitelisted
* @param workerEOA The worker EOA to check
* @return _isWhitelisted True if the worker EOA is whitelisted, false otherwise
*/
function isWorkerEOAWhitelisted(address workerEOA) external view returns (bool _isWhitelisted) {
_isWhitelisted = _workerEOAs[workerEOA];
}
/**
* @notice Check if a list of worker EOAs are whitelisted
* @param workerEOAs The list of worker EOAs to check
* @return An array of booleans, where each element corresponds to the whitelist status of the corresponding worker
* EOA
*/
/* solhint-disable-next-line gas-named-return-values */
function areWorkerEOAsWhitelisted(address[] calldata workerEOAs) external view returns (bool[] memory) {
bool[] memory whitelisted = new bool[](workerEOAs.length);
uint256 length = workerEOAs.length;
for (uint256 i; i < length; ++i) {
whitelisted[i] = _workerEOAs[workerEOAs[i]];
}
return whitelisted;
}
/// @notice Returns the version of the NodePaymaster
/// @return _version version of the NodePaymaster
/// @dev adds versioning to the NodePaymaster
function version() external pure returns (string memory _version) {
_version = "1.0.1";
}
}/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "./PackedUserOperation.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 if the UserOperation Paymaster's "postOp" call 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 PostOpRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
*/
event UserOperationPrefundTooLow(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce
);
/**
* 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.
* @param aggregator - The aggregator used for the following UserOperationEvents.
*/
event SignatureAggregatorChanged(address indexed aggregator);
/**
* A custom revert error of handleOps, to identify the offending op.
* 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.
* 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.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* A custom revert error of handleOps, to report a revert by account or paymaster.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. see FailedOp(uint256,string), above
* @param inner - data from inner cought revert reason
* @dev note that inner is truncated to 2048 bytes
*/
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
error PostOpReverted(bytes returnData);
/**
* Error case when a signature aggregator fails to verify the aggregated signature it had created.
* @param aggregator The aggregator that failed to verify the signature
*/
error SignatureValidationFailed(address aggregator);
// Return value of getSenderAddress.
error SenderAddressResult(address sender);
// UserOps handled, per aggregator.
struct UserOpsPerAggregator {
PackedUserOperation[] userOps;
// Aggregator address
IAggregator aggregator;
// Aggregated signature
bytes signature;
}
/**
* 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(
PackedUserOperation[] 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.
* @param userOp - The user operation to generate the request ID for.
* @return hash the hash of this UserOperation
*/
function getUserOpHash(
PackedUserOperation calldata userOp
) external view returns (bytes32);
/**
* Gas and return values during simulation.
* @param preOpGas - The gas used for validation (including preValidationGas)
* @param prefund - The required prefund for this operation
* @param accountValidationData - returned validationData from account.
* @param paymasterValidationData - return validationData from paymaster.
* @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
uint256 accountValidationData;
uint256 paymasterValidationData;
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;
error DelegateAndRevert(bool success, bytes ret);
/**
* Helper method for dry-run testing.
* @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result.
* The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace
* actual EntryPoint code is less convenient.
* @param target a target contract to make a delegatecall from entrypoint
* @param data data to pass to target in a delegatecall
*/
function delegateAndRevert(address target, bytes calldata data) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;
/* solhint-disable no-inline-assembly */
import "../interfaces/PackedUserOperation.sol";
import {calldataKeccak, min} from "./Helpers.sol";
/**
* Utility functions helpful when working with UserOperation structs.
*/
library UserOperationLib {
uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20;
uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36;
uint256 public constant PAYMASTER_DATA_OFFSET = 52;
/**
* Get sender from user operation data.
* @param userOp - The user operation data.
*/
function getSender(
PackedUserOperation 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.
* @param userOp - The user operation data.
*/
function gasPrice(
PackedUserOperation calldata userOp
) internal view returns (uint256) {
unchecked {
(uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = unpackUints(userOp.gasFees);
if (maxFeePerGas == maxPriorityFeePerGas) {
//legacy mode (for networks that don't support basefee opcode)
return maxFeePerGas;
}
return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
}
}
/**
* Pack the user operation data into bytes for hashing.
* @param userOp - The user operation data.
*/
function encode(
PackedUserOperation 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);
bytes32 accountGasLimits = userOp.accountGasLimits;
uint256 preVerificationGas = userOp.preVerificationGas;
bytes32 gasFees = userOp.gasFees;
bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
return abi.encode(
sender, nonce,
hashInitCode, hashCallData,
accountGasLimits, preVerificationGas, gasFees,
hashPaymasterAndData
);
}
function unpackUints(
bytes32 packed
) internal pure returns (uint256 high128, uint256 low128) {
return (uint128(bytes16(packed)), uint128(uint256(packed)));
}
//unpack just the high 128-bits from a packed value
function unpackHigh128(bytes32 packed) internal pure returns (uint256) {
return uint256(packed) >> 128;
}
// unpack just the low 128-bits from a packed value
function unpackLow128(bytes32 packed) internal pure returns (uint256) {
return uint128(uint256(packed));
}
function unpackMaxPriorityFeePerGas(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackHigh128(userOp.gasFees);
}
function unpackMaxFeePerGas(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackLow128(userOp.gasFees);
}
function unpackVerificationGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackHigh128(userOp.accountGasLimits);
}
function unpackCallGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackLow128(userOp.accountGasLimits);
}
function unpackPaymasterVerificationGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET]));
}
function unpackPostOpGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]));
}
function unpackPaymasterStaticFields(
bytes calldata paymasterAndData
) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) {
return (
address(bytes20(paymasterAndData[: PAYMASTER_VALIDATION_GAS_OFFSET])),
uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET])),
uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]))
);
}
/**
* Hash the user operation data.
* @param userOp - The user operation data.
*/
function hash(
PackedUserOperation calldata userOp
) internal pure returns (bytes32) {
return keccak256(encode(userOp));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import { BasePaymaster } from "account-abstraction/core/BasePaymaster.sol";
import { IEntryPoint } from "account-abstraction/interfaces/IEntryPoint.sol";
import { UserOperationLib } from "account-abstraction/core/UserOperationLib.sol";
import { PackedUserOperation } from "account-abstraction/core/UserOperationLib.sol";
import {
NODE_PM_MODE_USER,
NODE_PM_MODE_DAPP,
NODE_PM_MODE_KEEP,
NODE_PM_PREMIUM_PERCENT,
NODE_PM_PREMIUM_FIXED
} from "../types/Constants.sol";
/**
* @title BaseNode Paymaster
* @notice Base PM functionality for MEE Node PMs.
* It is used to sponsor userOps. Introduced for gas efficient MEE flow.
*/
abstract contract BaseNodePaymaster is BasePaymaster {
error InvalidNodePMRefundMode(bytes4 mode);
error InvalidNodePMPremiumMode(bytes4 mode);
error InvalidContext(uint256 length);
using UserOperationLib for PackedUserOperation;
using UserOperationLib for bytes32;
// 100% with 5 decimals precision
uint256 private constant _PREMIUM_CALCULATION_BASE = 10_000_000;
error EmptyMessageValue();
error InsufficientBalance();
error PaymasterVerificationGasLimitTooHigh();
error Disabled();
error PostOpGasLimitTooLow();
constructor(IEntryPoint _entryPoint, address _meeNodeMasterEOA) payable BasePaymaster(_entryPoint) {
_transferOwnership(_meeNodeMasterEOA);
}
/**
* @dev Accepts all userOps
* Verifies that the handleOps is called by the MEE Node, so it sponsors only for superTxns by owner MEE Node
* @dev The use of tx.origin makes the NodePaymaster incompatible with the general ERC4337 mempool.
* This is intentional, and the NodePaymaster is restricted to the MEE node owner anyway.
*
* PaymasterAndData is encoded as follows:
* 20 bytes: Paymaster address
* 32 bytes: pm gas values
* === PM_DATA_START ===
* 4 bytes: mode
* 4 bytes: premium mode
* 24 bytes: financial data:: premiumPercentage (only for according premium mode)
* 20 bytes: refundReceiver (only for DAPP refund mode)
*
* @param userOp the userOp to validate
* param userOpHash the hash of the userOp
* @param maxCost the max cost of the userOp
* @return context the context to be used in the postOp
* @return validationData the validationData to be used in the postOp
*/
// solhint-disable-next-line gas-named-return-values
function _validate(
PackedUserOperation calldata userOp,
bytes32, /*userOpHash*/
uint256 maxCost
)
internal
virtual
returns (bytes memory, uint256)
{
bytes4 refundMode;
bytes4 premiumMode;
bytes calldata pmAndData = userOp.paymasterAndData;
assembly {
// 0x34 = 52 => PAYMASTER_DATA_OFFSET
refundMode := calldataload(add(pmAndData.offset, 0x34))
}
address refundReceiver;
// Handle refund mode
if (refundMode == NODE_PM_MODE_KEEP) {
// NO REFUND
return ("", 0);
} else {
assembly {
// 0x38 = 56 => PAYMASTER_DATA_OFFSET + 4
premiumMode := calldataload(add(pmAndData.offset, 0x38))
}
if (refundMode == NODE_PM_MODE_USER) {
refundReceiver = userOp.sender;
} else if (refundMode == NODE_PM_MODE_DAPP) {
// if fixed premium => no financial data => offset is 0x08
// if % premium => financial data => offset is 0x08 + 0x18 = 0x20
uint256 refundReceiverOffset = premiumMode == NODE_PM_PREMIUM_FIXED ? 0x08 : 0x20;
assembly {
let o := add(0x34, refundReceiverOffset)
refundReceiver := shr(96, calldataload(add(pmAndData.offset, o)))
}
} else {
revert InvalidNodePMRefundMode(refundMode);
}
}
bytes memory context = _prepareContext({
refundReceiver: refundReceiver,
premiumMode: premiumMode,
maxCost: maxCost,
postOpGasLimit: userOp.unpackPostOpGasLimit(),
paymasterAndData: userOp.paymasterAndData
});
return (context, 0);
}
/**
* Post-operation handler.
* Checks mode and refunds the userOp.sender if needed.
* param PostOpMode enum with the following options: // not used
* 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.
* @dev postOpGasLimit is very important parameter that Node SHOULD use to balance its economic interests
* since penalty is not involved with refunds to sponsor here,
* postOpGasLimit should account for gas that is spend by AA-EP after benchmarking actualGasSpent
* if it is too low (still enough for _postOp), nodePM will be underpaid
* if it is too high, nodePM will be overcharging the superTxn sponsor as refund is going to be lower
* @param context - the context value returned by validatePaymasterUserOp
* context is encoded as follows:
* if mode is KEEP:
* 0 bytes
* ==== if there is a refund, always add ===
* 20 bytes: refundReceiver
* >== if % premium mode also add ===
* 24 bytes: financial data:: premiumPercentage
* 32 bytes: maxGasCost
* 32 bytes: postOpGasLimit
* (108 bytes total)
* >== if fixed premium ====
* 32 bytes: maxGasCost
* 32 bytes: postOpGasLimit
* (84 bytes total)
* @param actualGasCost - actual gas used so far (without this postOp call).
* @param actualUserOpFeePerGas - actual userOp fee per gas
*/
function _postOp(
PostOpMode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
)
internal
virtual
override
{
uint256 refund;
address refundReceiver;
// Prepare refund info if any
if (context.length == 0x00) { // 0 bytes => KEEP mode => NO REFUND
// do nothing
} else if (context.length == 0x54) {
// 84 bytes => REFUND: fixed premium mode.
(refundReceiver, refund) = _handleFixedPremium(context, actualGasCost, actualUserOpFeePerGas);
} else if (context.length == 0x6c) {
// 108 bytes => REFUND: % premium mode.
(refundReceiver, refund) = _handlePercentagePremium(context, actualGasCost, actualUserOpFeePerGas);
} else {
revert InvalidContext(context.length);
}
// send refund to the superTxn sponsor
if (refund > 0) {
// Note: At this point the paymaster hasn't received the refund yet, so this withdrawTo() is
// using the paymaster's existing balance. The paymaster's deposit in the entrypoint will be
// incremented after postOp() concludes.
entryPoint.withdrawTo(payable(refundReceiver), refund);
}
}
// ==== Helper functions ====
function _prepareContext(
address refundReceiver,
bytes4 premiumMode,
uint256 maxCost,
uint256 postOpGasLimit,
bytes calldata paymasterAndData
)
internal
pure
returns (bytes memory context)
{
context = abi.encodePacked(refundReceiver);
if (premiumMode == NODE_PM_PREMIUM_PERCENT) {
uint192 premiumPercentage;
// 0x3c = 60 => PAYMASTER_DATA_OFFSET + 8
assembly {
premiumPercentage := shr(64, calldataload(add(paymasterAndData.offset, 0x3c)))
}
context = abi.encodePacked(context, premiumPercentage, maxCost, postOpGasLimit); // 108 bytes
} else if (premiumMode == NODE_PM_PREMIUM_FIXED) {
context = abi.encodePacked(context, maxCost, postOpGasLimit); // 84 bytes
} else {
revert InvalidNodePMPremiumMode(premiumMode);
}
}
function _handleFixedPremium(
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
)
internal
pure
returns (address refundReceiver, uint256 refund)
{
uint256 maxGasCost;
uint256 postOpGasLimit;
assembly {
refundReceiver := shr(96, calldataload(context.offset))
maxGasCost := calldataload(add(context.offset, 0x14))
postOpGasLimit := calldataload(add(context.offset, 0x34))
}
// account for postOpGas
actualGasCost += postOpGasLimit * actualUserOpFeePerGas;
// when premium is fixed, payment by superTxn sponsor is maxGasCost + fixedPremium
// so we refund just the gas difference, while fixedPremium is going to the MEE Node
if (actualGasCost < maxGasCost) {
refund = maxGasCost - actualGasCost;
}
}
function _handlePercentagePremium(
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
)
internal
pure
returns (address refundReceiver, uint256 refund)
{
uint192 premiumPercentage;
uint256 maxGasCost;
uint256 postOpGasLimit;
assembly {
refundReceiver := shr(96, calldataload(context.offset))
premiumPercentage := shr(64, calldataload(add(context.offset, 0x14)))
maxGasCost := calldataload(add(context.offset, 0x2c))
postOpGasLimit := calldataload(add(context.offset, 0x4c))
}
// account for postOpGas
actualGasCost += postOpGasLimit * actualUserOpFeePerGas;
// we do not need to account for the penalty here because it goes to the beneficiary
// which is the MEE Node itself, so we do not have to charge user for the penalty
// account for MEE Node premium
uint256 costWithPremium = _applyPercentagePremium(actualGasCost, premiumPercentage);
// as MEE_NODE charges user with the premium
uint256 maxCostWithPremium = _applyPercentagePremium(maxGasCost, premiumPercentage);
// We do not check for the case, when costWithPremium > maxCost
// maxCost charged by the MEE Node should include the premium
// if this is done, costWithPremium can never be > maxCost
if (costWithPremium < maxCostWithPremium) {
refund = maxCostWithPremium - costWithPremium;
}
}
function _applyPercentagePremium(uint256 amount, uint256 premiumPercentage) internal pure returns (uint256 result) {
result = amount * (_PREMIUM_CALCULATION_BASE + premiumPercentage) / _PREMIUM_CALCULATION_BASE;
}
/// @dev This function is used to receive ETH from the user and immediately deposit it to the entryPoint
receive() external payable {
entryPoint.depositTo{ value: msg.value }(address(this));
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/**
* 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 accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
* @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid.
* Covers batch overhead.
* @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
* @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra 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 PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees;
bytes paymasterAndData;
bytes signature;
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.5;
/**
* 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 fits into one cell (used during handleOp)
* and the rest fit into a 2nd cell (used during stake/unstake)
* - 112 bit allows for 10^15 eth
* - 48 bit for full timestamp
* - 32 bit allows 150 years for unstake delay
*/
struct DepositInfo {
uint256 deposit;
bool staked;
uint112 stake;
uint32 unstakeDelaySec;
uint48 withdrawTime;
}
// API struct used by getStakeInfo and simulateValidation.
struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}
/**
* Get deposit info.
* @param account - The account to query.
* @return info - Full deposit information of given account.
*/
function getDepositInfo(
address account
) external view returns (DepositInfo memory info);
/**
* Get account balance.
* @param account - The account to query.
* @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.
* @param account - The account to add to.
*/
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.7.5;
import "./PackedUserOperation.sol";
/**
* Aggregated Signatures validator.
*/
interface IAggregator {
/**
* Validate aggregated signature.
* Revert if the aggregated signature does not match the given list of operations.
* @param userOps - Array of UserOperations to validate the signature for.
* @param signature - The aggregated signature.
*/
function validateSignatures(
PackedUserOperation[] calldata userOps,
bytes calldata signature
) external view;
/**
* Validate signature of a single userOp.
* This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
* the aggregator this account uses.
* 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(
PackedUserOperation 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(
PackedUserOperation[] calldata userOps
) external view returns (bytes memory aggregatedSignature);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
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: GPL-3.0
pragma solidity ^0.8.23;
/* solhint-disable no-inline-assembly */
/*
* For simulation purposes, validateUserOp (and validatePaymasterUserOp)
* must return this value in case of signature failure, instead of revert.
*/
uint256 constant SIG_VALIDATION_FAILED = 1;
/*
* For simulation purposes, validateUserOp (and validatePaymasterUserOp)
* return this value on success.
*/
uint256 constant SIG_VALIDATION_SUCCESS = 0;
/**
* Returned data from validateUserOp.
* validateUserOp returns a uint256, which 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.
* @param validationData - The packed validation data.
*/
function _parseValidationData(
uint256 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);
}
/**
* 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 ("memory-safe") {
let mem := mload(0x40)
let len := data.length
calldatacopy(mem, data.offset, len)
ret := keccak256(mem, len)
}
}
/**
* The minimum of two numbers.
* @param a - First number.
* @param b - Second number.
*/
function min(uint256 a, uint256 b) pure returns (uint256) {
return a < b ? a : b;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;
/* solhint-disable reason-string */
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../interfaces/IPaymaster.sol";
import "../interfaces/IEntryPoint.sol";
import "./UserOperationLib.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 public immutable entryPoint;
uint256 internal constant PAYMASTER_VALIDATION_GAS_OFFSET = UserOperationLib.PAYMASTER_VALIDATION_GAS_OFFSET;
uint256 internal constant PAYMASTER_POSTOP_GAS_OFFSET = UserOperationLib.PAYMASTER_POSTOP_GAS_OFFSET;
uint256 internal constant PAYMASTER_DATA_OFFSET = UserOperationLib.PAYMASTER_DATA_OFFSET;
constructor(IEntryPoint _entryPoint) Ownable(msg.sender) {
_validateEntryPointInterface(_entryPoint);
entryPoint = _entryPoint;
}
//sanity check: make sure this EntryPoint was compiled against the same
// IEntryPoint of this paymaster
function _validateEntryPointInterface(IEntryPoint _entryPoint) internal virtual {
require(IERC165(address(_entryPoint)).supportsInterface(type(IEntryPoint).interfaceId), "IEntryPoint interface mismatch");
}
/// @inheritdoc IPaymaster
function validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external override returns (bytes memory context, uint256 validationData) {
_requireFromEntryPoint();
return _validatePaymasterUserOp(userOp, userOpHash, maxCost);
}
/**
* Validate a user operation.
* @param userOp - The user operation.
* @param userOpHash - The hash of the user operation.
* @param maxCost - The maximum cost of the user operation.
*/
function _validatePaymasterUserOp(
PackedUserOperation 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,
uint256 actualUserOpFeePerGas
) external override {
_requireFromEntryPoint();
_postOp(mode, context, actualGasCost, actualUserOpFeePerGas);
}
/**
* 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. The paymaster still has to pay for gas.
* postOpReverted - never passed in a call to postOp().
* @param context - The context value returned by validatePaymasterUserOp
* @param actualGasCost - Actual gas used so far (without this postOp call).
* @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas
* and maxPriorityFee (and basefee)
* It is not the same as tx.gasprice, which is what the bundler pays.
*/
function _postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
) internal virtual {
(mode, context, actualGasCost, actualUserOpFeePerGas); // 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 - 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: MIT
pragma solidity ^0.8.27;
// STX Sig types
bytes3 constant SIG_TYPE_MEE_FLOW = 0x177eee;
bytes4 constant SIG_TYPE_SIMPLE = 0x177eee00;
bytes4 constant SIG_TYPE_ON_CHAIN = 0x177eee01;
bytes4 constant SIG_TYPE_ERC20_PERMIT = 0x177eee02;
// ...other sig types: ERC-7683, Permit2, etc
// EIP-1271 constants
bytes4 constant ERC1271_SUCCESS = 0x1626ba7e;
bytes4 constant ERC1271_FAILED = 0xffffffff;
// Node PM constants
bytes4 constant NODE_PM_MODE_USER = 0x170de000; // refund goes to the user
bytes4 constant NODE_PM_MODE_DAPP = 0x170de001; // refund goes to the dApp
bytes4 constant NODE_PM_MODE_KEEP = 0x170de002; // no refund as node sponsored
bytes4 constant NODE_PM_PREMIUM_PERCENT = 0x9ee4ce00; // premium percentage
bytes4 constant NODE_PM_PREMIUM_FIXED = 0x9ee4ce01;
// ERC-4337 validation constants
uint256 constant VALIDATION_SUCCESS = 0;
uint256 constant VALIDATION_FAILED = 1;
// Module type identifiers
uint256 constant MODULE_TYPE_MULTI = 0; // Module type identifier for Multitype install
uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
uint256 constant MODULE_TYPE_STATELESS_VALIDATOR = 7;
uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC1271 = 8;
uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC4337 = 9;
// Nexus Validation modes
bytes1 constant MODE_VALIDATION = 0x00;
bytes1 constant MODE_MODULE_ENABLE = 0x01;
bytes1 constant MODE_PREP = 0x02;
// ERC-7739 support constants
bytes4 constant SUPPORTS_ERC7739 = 0x77390000;
bytes4 constant SUPPORTS_ERC7739_V1 = 0x77390001;
// Typehashes
// keccak256("ModuleEnableMode(address module,uint256 moduleType,bytes32 userOpHash,bytes initData)")
bytes32 constant MODULE_ENABLE_MODE_TYPE_HASH = 0xf6c866c1cd985ce61f030431e576c0e82887de0643dfa8a2e6efc3463e638ed0;
// keccak256("EmergencyUninstall(address hook,uint256 hookType,bytes deInitData,uint256 nonce)")
bytes32 constant EMERGENCY_UNINSTALL_TYPE_HASH = 0xd3ddfc12654178cc44d4a7b6b969cfdce7ffe6342326ba37825314cffa0fba9c;// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling 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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.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 {
// User op succeeded.
opSucceeded,
// User op reverted. Still has to pay for gas.
opReverted,
// Only used internally in the EntryPoint (cleanup after postOp reverts). Never calling paymaster with this value
postOpReverted
}
/**
* 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,
* other values are invalid for paymaster.
* <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(
PackedUserOperation 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. The paymaster still has to pay for gas.
* postOpReverted - never passed in a call to postOp().
* @param context - The context value returned by validatePaymasterUserOp
* @param actualGasCost - Actual gas used so far (without this postOp call).
* @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas
* and maxPriorityFee (and basefee)
* It is not the same as tx.gasprice, which is what the bundler pays.
*/
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}{
"remappings": [
"forge-std/=node_modules/forge-std/src/",
"account-abstraction/=node_modules/account-abstraction/contracts/",
"solady/=node_modules/solady/src/",
"sentinellist/=node_modules/@rhinestone/sentinellist/src/",
"module-bases/=node_modules/@rhinestone/module-bases/src/",
"erc7739Validator/=node_modules/@erc7579/erc7739-validator-base/src/",
"EnumerableSet4337/=node_modules/@erc7579/enumerablemap4337/src/",
"erc7579/=node_modules/@erc7579/implementation/src/",
"byteslib/=node_modules/solidity-bytes-utils/contracts/",
"rlp-reader/=node_modules/solidity-rlp/contracts/",
"murky-trees/=node_modules/murky/src/",
"solarray/=node_modules/solarray/src/",
"excessively-safe-call/=node_modules/excessively-safe-call/src/",
"@ERC4337/=node_modules/@ERC4337/",
"@erc7579/=node_modules/@erc7579/",
"@gnosis.pm/=node_modules/@gnosis.pm/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@prb/=node_modules/@prb/",
"@rhinestone/=node_modules/@rhinestone/",
"@safe-global/=node_modules/@safe-global/",
"@zerodev/=node_modules/@zerodev/",
"account-abstraction-v0.6/=node_modules/account-abstraction-v0.6/",
"ds-test/=node_modules/ds-test/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"solidity-bytes-utils/=node_modules/solidity-bytes-utils/",
"solidity-rlp/=node_modules/solidity-rlp/"
],
"optimizer": {
"enabled": true,
"runs": 999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"NodePMDeployFailed","type":"error"},{"inputs":[{"internalType":"address","name":"entryPoint","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address[]","name":"workerEoas","type":"address[]"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"deployAndFundNodePaymaster","outputs":[{"internalType":"address","name":"nodePaymaster","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"entryPoint","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address[]","name":"workerEoas","type":"address[]"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getNodePaymasterAddress","outputs":[{"internalType":"address","name":"nodePmAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"_version","type":"string"}],"stateMutability":"pure","type":"function"}]Contract Creation Code
6080806040523460155761188d908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806354fd4d5014610044578063847744031461003f5763922d945d1461003a575f80fd5b6102c7565b610149565b346100b9575f3660031901126100b95760405161006081610321565b60058152604060208201917f312e302e3100000000000000000000000000000000000000000000000000000083528151928391602083525180918160208501528484015e5f828201840152601f01601f19168101030190f35b5f80fd5b5f9103126100b957565b35906001600160a01b03821682036100b957565b9060806003198301126100b9576100f260046100c7565b916100fd60246100c7565b9160443567ffffffffffffffff81116100b957826023820112156100b95780600401359267ffffffffffffffff84116100b95760248460051b830101116100b957602401919060643590565b6101a26101c66101c061015b366100db565b949395929690916101b06101728785848c8c6103ee565b986101a261140f9561018660208801610364565b9680885261047260208901396040519485938c60208601610374565b03601f198101835282610342565b60405194859360208501906103d1565b906103d1565b8051906020015ff5916001600160a01b03831680159182156102b3575b505061028b576001600160a01b031690813b156100b9576040517fb760faf90000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152915f908390602490829034905af1918215610286576102689261026c575b506040516001600160a01b0390911681529081906020820190565b0390f35b8061027a5f61028093610342565b806100bd565b5f61024d565b6103e3565b7f25ba697b000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160a01b0316141590505f806101e3565b346100b95760206102e36102da366100db565b939290926103ee565b6001600160a01b0360405191168152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761033d57604052565b6102f4565b90601f8019910116810190811067ffffffffffffffff82111761033d57604052565b906103726040519283610342565b565b6001600160a01b039182168152911660208201526060604082018190528101839052608001915f5b8181106103a95750505090565b9091926020806001926001600160a01b036103c3886100c7565b16815201940192910161039c565b805191908290602001825e015f815290565b6040513d5f823e3d90fd5b6101a291610431610445926101a26001600160a01b03989761140f9761041660208a01610364565b98808a5261047260208b013960405195869460208601610374565b6040519283916101c06020840180976103d1565b5190206040519160ff83533060601b60018401526015830152603582015260558082209101604052169056fe60a08060405261140f80380380916100178285610279565b8339810190606081830312610223578051906001600160a01b038216808303610223576100466020830161029c565b604083015190926001600160401b03821161022357019380601f86011215610223578451946001600160401b038611610265578560051b906040519661008f6020840189610279565b875260208088019282010192831161022357602001905b82821061024d57505050331561023a5760206024916100c4336102b0565b6040516301ffc9a760e01b815263122a0e9b60e31b600482015292839182905afa90811561022f575f916101f0575b50156101ab57610105916080526102b0565b80515f5b8181106101605760405161111890816102f7823960805181818161025b015281816103040152818161040d015281816104950152818161060c01528181610a3601528181610acd01528181610c730152610d340152f35b825181101561019757600190818060a01b0360208260051b86010151165f528160205260405f208260ff1982541617905501610109565b634e487b7160e01b5f52603260045260245ffd5b60405162461bcd60e51b815260206004820152601e60248201527f49456e747279506f696e7420696e74657266616365206d69736d6174636800006044820152606490fd5b90506020813d602011610227575b8161020b60209383610279565b8101031261022357518015158103610223575f6100f3565b5f80fd5b3d91506101fe565b6040513d5f823e3d90fd5b631e4fbdf760e01b5f525f60045260245ffd5b6020809161025a8461029c565b8152019101906100a6565b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761026557604052565b51906001600160a01b038216820361022357565b5f80546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60806040526004361015610022575b3615610018575f80fd5b610020610c69565b005b5f5f3560e01c80630396cb6014610a9d578063205c287814610a095780632488d784146109c5578063300d8479146108dd57806352b7512c1461083057806354fd4d50146107cb578063715018a6146107655780637c627b211461057857806383e22319146105205780638da5cb5b146104fa5780639bdfeb94146104b9578063b0d691fe14610475578063bb9fe6bf146103e9578063bc300d981461038e578063c23a5cea146102d7578063c399ec881461020e578063cc69d1fd146101c7578063d0e30db0146101b05763f2fde38b146100fe575061000e565b346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab5761012b610ceb565b801561017f576001600160a01b0382548273ffffffffffffffffffffffffffffffffffffffff198216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b50806003193601126101ad576101c4610c69565b80f35b50346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab576101f5610ceb565b8152600160205260408120600160ff1982541617905580f35b50346101ad57806003193601126101ad576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156102cc578291610296575b602082604051908152f35b90506020813d6020116102c4575b816102b160209383610be3565b810103126101ab5760209150515f61028b565b3d91506102a4565b6040513d84823e3d90fd5b50346101ad5760203660031901126101ad57806102f2610b55565b6102fa610ceb565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b1561038a576001600160a01b03602484928360405195869485937fc23a5cea0000000000000000000000000000000000000000000000000000000085521660048401525af180156102cc576103795750f35b8161038391610be3565b6101ad5780f35b5050fd5b50346101ad5761039d36610b6b565b6103a5610ceb565b825b8181106103b2578380f35b806001600160a01b036103d06103cb6001948688610c31565b610c55565b168552816020526040852060ff198154169055016103a7565b50346101ad57806003193601126101ad57610402610ceb565b806001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610472578180916004604051809481937fbb9fe6bf0000000000000000000000000000000000000000000000000000000083525af180156102cc576103795750f35b50fd5b50346101ad57806003193601126101ad5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab5760408260ff9260209452600184522054166040519015158152f35b50346101ad57806003193601126101ad576001600160a01b036020915416604051908152f35b50346101ad5761052f36610b6b565b610537610ceb565b825b818110610544578380f35b806001600160a01b0361055d6103cb6001948688610c31565b16855281602052604085208260ff1982541617905501610539565b50346101ad5760803660031901126101ad57600360043510156101ad576024359067ffffffffffffffff82116101ad57366023830112156101ad5781600401359167ffffffffffffffff83116101ab576024810192366024828401011161076157604435936064356105e8610d2a565b8493859380610677575b5050505082935081610602575050f35b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691823b156106725760405163040b850f60e31b81526001600160a01b0392909216600483015260248201529082908290604490829084905af180156102cc576103795750f35b505050fd5b92945090925090605481036106d157506106a684956106a08694605860388601359501356110a7565b906110ce565b908082106106c0575b5050903560601c5b5f8080806105f2565b6106ca92506110db565b5f806106af565b919491606c810361073657506107116106a0605061070987986107048997603888013560401c95869360708a01356110a7565b6110e8565b9301356110e8565b808210610725575b5050903560601c6106b7565b61072f92506110db565b5f80610719565b7f6eb313c6000000000000000000000000000000000000000000000000000000008552600452602484fd5b8280fd5b50346101ad57806003193601126101ad5761077e610ceb565b806001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346101ad57806003193601126101ad575061082c6040516107ee604082610be3565b600581527f312e302e310000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190610bbf565b0390f35b50346101ad5760603660031901126101ad5760043567ffffffffffffffff81116101ab5761012060031982360301126101ab57906108b391610870610d2a565b6060916001600160a01b0381541632149081156108c5575b50156108bd5761089f915060443590600401610ded565b604051928392604084526040840190610bbf565b9060208301520390f35b50600161089f565b60ff915060409032815260016020522054165f610888565b50346101ad576108ec36610b6b565b906108f682610c19565b906109046040519283610be3565b82825261091083610c19565b602083019390601f1901368537845b81811061096d5750505090604051928392602084019060208552518091526040840192915b818110610952575050500390f35b82511515845285945060209384019390920191600101610944565b6001600160a01b036109836103cb838587610c31565b168652600160205260ff60408720541684518210156109b1571515600582901b85016020015260010161091f565b602487634e487b7160e01b81526032600452fd5b50346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab576109f3610ceb565b8152600160205260408120805460ff1916905580f35b50346101ad5760403660031901126101ad5780610a24610b55565b610a2c610ceb565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b1561038a5760405163040b850f60e31b81526001600160a01b03929092166004830152602480359083015282908290604490829084905af180156102cc576103795750f35b506020366003190112610b515760043563ffffffff8116809103610b5157610ac3610ceb565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b15610b51575f906024604051809481937f0396cb60000000000000000000000000000000000000000000000000000000008352600483015234905af18015610b4657610b3a575080f35b61002091505f90610be3565b6040513d5f823e3d90fd5b5f80fd5b600435906001600160a01b0382168203610b5157565b906020600319830112610b515760043567ffffffffffffffff8111610b515782602382011215610b515780600401359267ffffffffffffffff8411610b515760248460051b83010111610b51576024019190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90601f8019910116810190811067ffffffffffffffff821117610c0557604052565b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff8111610c055760051b60200190565b9190811015610c415760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b0381168103610b515790565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610b51575f602491604051928380927fb760faf900000000000000000000000000000000000000000000000000000000825230600483015234905af18015610b4657610cdf5750565b5f610ce991610be3565b565b6001600160a01b035f54163303610cfe57565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610d5c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53656e646572206e6f7420456e747279506f696e7400000000000000000000006044820152fd5b903590601e1981360301821215610b51570180359067ffffffffffffffff8211610b5157602001918136038313610b5157565b60e0810191610dfc8383610dba565b5060348101357fffffffff000000000000000000000000000000000000000000000000000000001692907f170de002000000000000000000000000000000000000000000000000000000008403610e68575050505050604051610e60602082610be3565b5f8152905f90565b6038810135937f170de000000000000000000000000000000000000000000000000000000000008103611006575050610ea081610c55565b935b610eac8183610dba565b603492919211610b51576024610ec792013560801c92610dba565b509160405160208101956bffffffffffffffffffffffff199060601b16865260148152610ef5603482610be3565b7fffffffff000000000000000000000000000000000000000000000000000000008195167f9ee4ce000000000000000000000000000000000000000000000000000000000081145f14610f9657505060209450926058928592610f91956040519784899551918291018787015e840192603c67ffffffffffffffff19910135168584015260388301528482015203016038810184520182610be3565b905f90565b909450909250639ee4ce0160e01b8103610fdb5750610f919260409260209284519687935180918686015e830191848301528482015203016020810184520182610be3565b7ffb96c33d000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f170de00100000000000000000000000000000000000000000000000000000000810361107c57506034907fffffffff000000000000000000000000000000000000000000000000000000008516639ee4ce0160e01b036110735760ff60085b1601013560601c93610ea2565b60ff6020611066565b7f83aa2c17000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b818102929181159184041417156110ba57565b634e487b7160e01b5f52601160045260245ffd5b919082018092116110ba57565b919082039182116110ba57565b906298968001908162989680116110ba576298968091611107916110a7565b049056fea164736f6c634300081b000aa164736f6c634300081b000a
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806354fd4d5014610044578063847744031461003f5763922d945d1461003a575f80fd5b6102c7565b610149565b346100b9575f3660031901126100b95760405161006081610321565b60058152604060208201917f312e302e3100000000000000000000000000000000000000000000000000000083528151928391602083525180918160208501528484015e5f828201840152601f01601f19168101030190f35b5f80fd5b5f9103126100b957565b35906001600160a01b03821682036100b957565b9060806003198301126100b9576100f260046100c7565b916100fd60246100c7565b9160443567ffffffffffffffff81116100b957826023820112156100b95780600401359267ffffffffffffffff84116100b95760248460051b830101116100b957602401919060643590565b6101a26101c66101c061015b366100db565b949395929690916101b06101728785848c8c6103ee565b986101a261140f9561018660208801610364565b9680885261047260208901396040519485938c60208601610374565b03601f198101835282610342565b60405194859360208501906103d1565b906103d1565b8051906020015ff5916001600160a01b03831680159182156102b3575b505061028b576001600160a01b031690813b156100b9576040517fb760faf90000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152915f908390602490829034905af1918215610286576102689261026c575b506040516001600160a01b0390911681529081906020820190565b0390f35b8061027a5f61028093610342565b806100bd565b5f61024d565b6103e3565b7f25ba697b000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160a01b0316141590505f806101e3565b346100b95760206102e36102da366100db565b939290926103ee565b6001600160a01b0360405191168152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761033d57604052565b6102f4565b90601f8019910116810190811067ffffffffffffffff82111761033d57604052565b906103726040519283610342565b565b6001600160a01b039182168152911660208201526060604082018190528101839052608001915f5b8181106103a95750505090565b9091926020806001926001600160a01b036103c3886100c7565b16815201940192910161039c565b805191908290602001825e015f815290565b6040513d5f823e3d90fd5b6101a291610431610445926101a26001600160a01b03989761140f9761041660208a01610364565b98808a5261047260208b013960405195869460208601610374565b6040519283916101c06020840180976103d1565b5190206040519160ff83533060601b60018401526015830152603582015260558082209101604052169056fe60a08060405261140f80380380916100178285610279565b8339810190606081830312610223578051906001600160a01b038216808303610223576100466020830161029c565b604083015190926001600160401b03821161022357019380601f86011215610223578451946001600160401b038611610265578560051b906040519661008f6020840189610279565b875260208088019282010192831161022357602001905b82821061024d57505050331561023a5760206024916100c4336102b0565b6040516301ffc9a760e01b815263122a0e9b60e31b600482015292839182905afa90811561022f575f916101f0575b50156101ab57610105916080526102b0565b80515f5b8181106101605760405161111890816102f7823960805181818161025b015281816103040152818161040d015281816104950152818161060c01528181610a3601528181610acd01528181610c730152610d340152f35b825181101561019757600190818060a01b0360208260051b86010151165f528160205260405f208260ff1982541617905501610109565b634e487b7160e01b5f52603260045260245ffd5b60405162461bcd60e51b815260206004820152601e60248201527f49456e747279506f696e7420696e74657266616365206d69736d6174636800006044820152606490fd5b90506020813d602011610227575b8161020b60209383610279565b8101031261022357518015158103610223575f6100f3565b5f80fd5b3d91506101fe565b6040513d5f823e3d90fd5b631e4fbdf760e01b5f525f60045260245ffd5b6020809161025a8461029c565b8152019101906100a6565b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761026557604052565b51906001600160a01b038216820361022357565b5f80546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60806040526004361015610022575b3615610018575f80fd5b610020610c69565b005b5f5f3560e01c80630396cb6014610a9d578063205c287814610a095780632488d784146109c5578063300d8479146108dd57806352b7512c1461083057806354fd4d50146107cb578063715018a6146107655780637c627b211461057857806383e22319146105205780638da5cb5b146104fa5780639bdfeb94146104b9578063b0d691fe14610475578063bb9fe6bf146103e9578063bc300d981461038e578063c23a5cea146102d7578063c399ec881461020e578063cc69d1fd146101c7578063d0e30db0146101b05763f2fde38b146100fe575061000e565b346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab5761012b610ceb565b801561017f576001600160a01b0382548273ffffffffffffffffffffffffffffffffffffffff198216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b50806003193601126101ad576101c4610c69565b80f35b50346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab576101f5610ceb565b8152600160205260408120600160ff1982541617905580f35b50346101ad57806003193601126101ad576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156102cc578291610296575b602082604051908152f35b90506020813d6020116102c4575b816102b160209383610be3565b810103126101ab5760209150515f61028b565b3d91506102a4565b6040513d84823e3d90fd5b50346101ad5760203660031901126101ad57806102f2610b55565b6102fa610ceb565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b1561038a576001600160a01b03602484928360405195869485937fc23a5cea0000000000000000000000000000000000000000000000000000000085521660048401525af180156102cc576103795750f35b8161038391610be3565b6101ad5780f35b5050fd5b50346101ad5761039d36610b6b565b6103a5610ceb565b825b8181106103b2578380f35b806001600160a01b036103d06103cb6001948688610c31565b610c55565b168552816020526040852060ff198154169055016103a7565b50346101ad57806003193601126101ad57610402610ceb565b806001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610472578180916004604051809481937fbb9fe6bf0000000000000000000000000000000000000000000000000000000083525af180156102cc576103795750f35b50fd5b50346101ad57806003193601126101ad5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab5760408260ff9260209452600184522054166040519015158152f35b50346101ad57806003193601126101ad576001600160a01b036020915416604051908152f35b50346101ad5761052f36610b6b565b610537610ceb565b825b818110610544578380f35b806001600160a01b0361055d6103cb6001948688610c31565b16855281602052604085208260ff1982541617905501610539565b50346101ad5760803660031901126101ad57600360043510156101ad576024359067ffffffffffffffff82116101ad57366023830112156101ad5781600401359167ffffffffffffffff83116101ab576024810192366024828401011161076157604435936064356105e8610d2a565b8493859380610677575b5050505082935081610602575050f35b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691823b156106725760405163040b850f60e31b81526001600160a01b0392909216600483015260248201529082908290604490829084905af180156102cc576103795750f35b505050fd5b92945090925090605481036106d157506106a684956106a08694605860388601359501356110a7565b906110ce565b908082106106c0575b5050903560601c5b5f8080806105f2565b6106ca92506110db565b5f806106af565b919491606c810361073657506107116106a0605061070987986107048997603888013560401c95869360708a01356110a7565b6110e8565b9301356110e8565b808210610725575b5050903560601c6106b7565b61072f92506110db565b5f80610719565b7f6eb313c6000000000000000000000000000000000000000000000000000000008552600452602484fd5b8280fd5b50346101ad57806003193601126101ad5761077e610ceb565b806001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346101ad57806003193601126101ad575061082c6040516107ee604082610be3565b600581527f312e302e310000000000000000000000000000000000000000000000000000006020820152604051918291602083526020830190610bbf565b0390f35b50346101ad5760603660031901126101ad5760043567ffffffffffffffff81116101ab5761012060031982360301126101ab57906108b391610870610d2a565b6060916001600160a01b0381541632149081156108c5575b50156108bd5761089f915060443590600401610ded565b604051928392604084526040840190610bbf565b9060208301520390f35b50600161089f565b60ff915060409032815260016020522054165f610888565b50346101ad576108ec36610b6b565b906108f682610c19565b906109046040519283610be3565b82825261091083610c19565b602083019390601f1901368537845b81811061096d5750505090604051928392602084019060208552518091526040840192915b818110610952575050500390f35b82511515845285945060209384019390920191600101610944565b6001600160a01b036109836103cb838587610c31565b168652600160205260ff60408720541684518210156109b1571515600582901b85016020015260010161091f565b602487634e487b7160e01b81526032600452fd5b50346101ad5760203660031901126101ad576004356001600160a01b0381168091036101ab576109f3610ceb565b8152600160205260408120805460ff1916905580f35b50346101ad5760403660031901126101ad5780610a24610b55565b610a2c610ceb565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b1561038a5760405163040b850f60e31b81526001600160a01b03929092166004830152602480359083015282908290604490829084905af180156102cc576103795750f35b506020366003190112610b515760043563ffffffff8116809103610b5157610ac3610ceb565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b15610b51575f906024604051809481937f0396cb60000000000000000000000000000000000000000000000000000000008352600483015234905af18015610b4657610b3a575080f35b61002091505f90610be3565b6040513d5f823e3d90fd5b5f80fd5b600435906001600160a01b0382168203610b5157565b906020600319830112610b515760043567ffffffffffffffff8111610b515782602382011215610b515780600401359267ffffffffffffffff8411610b515760248460051b83010111610b51576024019190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90601f8019910116810190811067ffffffffffffffff821117610c0557604052565b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff8111610c055760051b60200190565b9190811015610c415760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b0381168103610b515790565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610b51575f602491604051928380927fb760faf900000000000000000000000000000000000000000000000000000000825230600483015234905af18015610b4657610cdf5750565b5f610ce991610be3565b565b6001600160a01b035f54163303610cfe57565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610d5c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53656e646572206e6f7420456e747279506f696e7400000000000000000000006044820152fd5b903590601e1981360301821215610b51570180359067ffffffffffffffff8211610b5157602001918136038313610b5157565b60e0810191610dfc8383610dba565b5060348101357fffffffff000000000000000000000000000000000000000000000000000000001692907f170de002000000000000000000000000000000000000000000000000000000008403610e68575050505050604051610e60602082610be3565b5f8152905f90565b6038810135937f170de000000000000000000000000000000000000000000000000000000000008103611006575050610ea081610c55565b935b610eac8183610dba565b603492919211610b51576024610ec792013560801c92610dba565b509160405160208101956bffffffffffffffffffffffff199060601b16865260148152610ef5603482610be3565b7fffffffff000000000000000000000000000000000000000000000000000000008195167f9ee4ce000000000000000000000000000000000000000000000000000000000081145f14610f9657505060209450926058928592610f91956040519784899551918291018787015e840192603c67ffffffffffffffff19910135168584015260388301528482015203016038810184520182610be3565b905f90565b909450909250639ee4ce0160e01b8103610fdb5750610f919260409260209284519687935180918686015e830191848301528482015203016020810184520182610be3565b7ffb96c33d000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f170de00100000000000000000000000000000000000000000000000000000000810361107c57506034907fffffffff000000000000000000000000000000000000000000000000000000008516639ee4ce0160e01b036110735760ff60085b1601013560601c93610ea2565b60ff6020611066565b7f83aa2c17000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b818102929181159184041417156110ba57565b634e487b7160e01b5f52601160045260245ffd5b919082018092116110ba57565b919082039182116110ba57565b906298968001908162989680116110ba576298968091611107916110a7565b049056fea164736f6c634300081b000aa164736f6c634300081b000a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.