Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 39231422 | 18 mins ago | 0 ETH | |||||
| 39231422 | 18 mins ago | 0 ETH | |||||
| 39231422 | 18 mins ago | 0 ETH | |||||
| 39231422 | 18 mins ago | 0 ETH | |||||
| 39231339 | 19 mins ago | 0 ETH | |||||
| 39231339 | 19 mins ago | 0 ETH | |||||
| 39231339 | 19 mins ago | 0 ETH | |||||
| 39231339 | 19 mins ago | 0 ETH | |||||
| 39227696 | 1 hr ago | 0 ETH | |||||
| 39227696 | 1 hr ago | 0 ETH | |||||
| 39227696 | 1 hr ago | 0 ETH | |||||
| 39227696 | 1 hr ago | 0 ETH | |||||
| 39227674 | 1 hr ago | 0 ETH | |||||
| 39227674 | 1 hr ago | 0 ETH | |||||
| 39227674 | 1 hr ago | 0 ETH | |||||
| 39227674 | 1 hr ago | 0 ETH | |||||
| 39224181 | 2 hrs ago | 0 ETH | |||||
| 39224181 | 2 hrs ago | 0 ETH | |||||
| 39224181 | 2 hrs ago | 0 ETH | |||||
| 39224181 | 2 hrs ago | 0 ETH | |||||
| 39224145 | 2 hrs ago | 0 ETH | |||||
| 39224145 | 2 hrs ago | 0 ETH | |||||
| 39224145 | 2 hrs ago | 0 ETH | |||||
| 39224145 | 2 hrs ago | 0 ETH | |||||
| 39220591 | 3 hrs ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
SingleSignerValidationModule
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 10000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol";
import {IValidationModule} from "@erc6900/reference-implementation/interfaces/IValidationModule.sol";
import {ReplaySafeWrapper} from "@erc6900/reference-implementation/modules/ReplaySafeWrapper.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {SignatureType} from "../../helpers/SignatureType.sol";
import {ModuleBase} from "../ModuleBase.sol";
/// @title Single Signer Validation Module
/// @author Alchemy
/// @notice This validation enables any ECDSA (secp256k1 curve) signature validation or Contract Owner signature
/// validation. It handles installation by each entity (entityId).
/// NOTE:
/// - The first byte of the to be checked Signature is the SignatureType, indicating EOA or Contract Owner.
/// - Uninstallation will NOT disable all installed entity IDs of an account. It only uninstalls the
/// entity ID that is passed in. Account must remove access for each entity ID if want to disable all.
/// - None of the functions are installed on the account. Account states are to be retrieved from this global
/// singleton directly.
/// - This validation supports ERC-1271. The signature is valid if it is signed by the owner's private key.
/// - This validation supports composition that other validation can relay on entities in this validation to
/// validate partially or fully.
contract SingleSignerValidationModule is IValidationModule, ReplaySafeWrapper, ModuleBase {
using MessageHashUtils for bytes32;
uint256 internal constant _SIG_VALIDATION_PASSED = 0;
uint256 internal constant _SIG_VALIDATION_FAILED = 1;
// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e;
bytes4 internal constant _1271_INVALID = 0xffffffff;
mapping(uint32 entityId => mapping(address account => address)) public signers;
/// @notice This event is emitted when Signer of the account's validation changes.
/// @param account The account whose validation Signer changed.
/// @param entityId The entityId for the account and the signer.
/// @param newSigner The address of the new signer.
/// @param previousSigner The address of the previous signer.
event SignerTransferred(
address indexed account, uint32 indexed entityId, address indexed newSigner, address previousSigner
) anonymous;
error InvalidSignatureType();
error NotAuthorized();
/// @notice Transfer Signer of the account's validation to `newSigner`.
/// @param entityId The entityId for the account and the signer.
/// @param newSigner The address of the new signer.
function transferSigner(uint32 entityId, address newSigner) external {
_transferSigner(entityId, newSigner);
}
/// @inheritdoc IModule
function onInstall(bytes calldata data) external override {
(uint32 entityId, address newSigner) = abi.decode(data, (uint32, address));
_transferSigner(entityId, newSigner);
}
/// @inheritdoc IModule
function onUninstall(bytes calldata data) external override {
uint32 entityId = abi.decode(data, (uint32));
_transferSigner(entityId, address(0));
}
/// @inheritdoc IValidationModule
function validateUserOp(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash)
external
view
override
returns (uint256)
{
// Validate the user op signature against the owner.
if (_checkSig(signers[entityId][userOp.sender], userOpHash.toEthSignedMessageHash(), userOp.signature)) {
return _SIG_VALIDATION_PASSED;
}
return _SIG_VALIDATION_FAILED;
}
/// @inheritdoc IValidationModule
function validateRuntime(
address account,
uint32 entityId,
address sender,
uint256,
bytes calldata,
bytes calldata
) external view override {
// Validate that the sender is the owner of the account or self.
if (sender != signers[entityId][account]) {
revert NotAuthorized();
}
return;
}
/// @inheritdoc IValidationModule
/// @dev The signature is valid if it is signed by the owner's private key
/// (if the owner is an EOA) or if it is a valid ERC-1271 signature from the
/// owner (if the owner is a contract).
/// Note that the digest is wrapped in an EIP-712 struct to prevent cross-account replay attacks. The
/// replay-safe hash may be retrieved by calling the public function `replaySafeHash`.
function validateSignature(address account, uint32 entityId, address, bytes32 digest, bytes calldata signature)
external
view
override
returns (bytes4)
{
bytes32 _replaySafeHash = replaySafeHash(account, digest);
if (_checkSig(signers[entityId][account], _replaySafeHash, signature)) {
return _1271_MAGIC_VALUE;
}
return _1271_INVALID;
}
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ Module interface functions ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
/// @inheritdoc IModule
function moduleId() external pure returns (string memory) {
return "alchemy.single-signer-validation-module.1.0.0";
}
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ModuleBase, IERC165)
returns (bool)
{
return (interfaceId == type(IValidationModule).interfaceId || super.supportsInterface(interfaceId));
}
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ Internal / Private functions ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
function _transferSigner(uint32 entityId, address newSigner) internal {
address previousSigner = signers[entityId][msg.sender];
signers[entityId][msg.sender] = newSigner;
emit SignerTransferred(msg.sender, entityId, newSigner, previousSigner);
}
function _checkSig(address owner, bytes32 digest, bytes calldata sig) internal view returns (bool) {
if (sig.length < 1) {
revert InvalidSignatureType();
}
SignatureType sigType = SignatureType(uint8(bytes1(sig)));
if (sigType == SignatureType.EOA) {
(address recovered, ECDSA.RecoverError err,) = ECDSA.tryRecover(digest, sig[1:]);
if (err == ECDSA.RecoverError.NoError && recovered == owner) {
return true;
}
return false;
} else if (sigType == SignatureType.CONTRACT_OWNER) {
return SignatureChecker.isValidERC1271SignatureNow(owner, digest, sig[1:]);
}
revert InvalidSignatureType();
}
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
interface IModule is IERC165 {
/// @notice Initialize module data for the modular account.
/// @dev Called by the modular account during `installExecution`.
/// @param data Optional bytes array to be decoded and used by the module to setup initial module data for the
/// modular account.
function onInstall(bytes calldata data) external;
/// @notice Clear module data for the modular account.
/// @dev Called by the modular account during `uninstallExecution`.
/// @param data Optional bytes array to be decoded and used by the module to clear module data for the modular
/// account.
function onUninstall(bytes calldata data) external;
/// @notice Return a unique identifier for the module.
/// @dev This function MUST return a string in the format "vendor.module.semver". The vendor and module
/// names MUST NOT contain a period character.
/// @return The module ID.
function moduleId() external view returns (string memory);
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {IModule} from "./IModule.sol";
interface IValidationModule is IModule {
/// @notice Run the user operation validation function specified by the `entityId`.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param userOp The user operation.
/// @param userOpHash The user operation hash.
/// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes).
function validateUserOp(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash)
external
returns (uint256);
/// @notice Run the runtime validation function specified by the `entityId`.
/// @dev To indicate the entire call should revert, the function MUST revert.
/// @param account the account to validate for.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param sender The caller address.
/// @param value The call value.
/// @param data The calldata sent.
/// @param authorization Additional data for the validation function to use.
function validateRuntime(
address account,
uint32 entityId,
address sender,
uint256 value,
bytes calldata data,
bytes calldata authorization
) external;
/// @notice Validates a signature using ERC-1271.
/// @dev To indicate the entire call should revert, the function MUST revert.
/// @param account the account to validate for.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param sender the address that sent the ERC-1271 request to the smart account
/// @param hash the hash of the ERC-1271 request
/// @param signature the signature of the ERC-1271 request
/// @return The ERC-1271 `MAGIC_VALUE` if the signature is valid, or 0xFFFFFFFF if invalid.
function validateSignature(
address account,
uint32 entityId,
address sender,
bytes32 hash,
bytes calldata signature
) external view returns (bytes4);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {ModuleEIP712} from "./ModuleEIP712.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
// A contract mixin for modules that wish to use EIP-712 to wrap the hashes sent to the EIP-1271 function
// `isValidSignature`.
// This makes signatures generated by owners of contract accounts non-replayable across multiple accounts owned by
// the same owner.
abstract contract ReplaySafeWrapper is ModuleEIP712 {
// keccak256("ReplaySafeHash(bytes32 hash)")
bytes32 private constant _REPLAY_SAFE_HASH_TYPEHASH =
0x294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff;
/// @notice Wraps a hash in an EIP-712 envelope to prevent cross-account replay attacks.
/// Uses the ModuleEIP712 domain separator, which includes the chainId, module address, and account address.
/// @param account The account that will validate the message hash.
/// @param hash The hash to wrap.
/// @return The the replay-safe hash, computed by wrapping the input hash in an EIP-712 struct.
function replaySafeHash(address account, bytes32 hash) public view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash({
domainSeparator: _domainSeparator(account),
structHash: _hashStruct(hash)
});
}
function _hashStruct(bytes32 hash) internal pure virtual returns (bytes32) {
bytes32 res;
assembly ("memory-safe") {
mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH)
mstore(0x20, hash)
res := keccak256(0x00, 0x40)
}
return res;
}
}// 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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
/// @notice An enum that is prepended to signatures to differentiate between EOA and contract owner signatures.
enum SignatureType {
EOA,
CONTRACT_OWNER
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {IAccountExecute} from "@eth-infinitism/account-abstraction/interfaces/IAccountExecute.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {ERC165, IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol";
/// @title Module Base
/// @author Alchemy
/// @dev Implements ERC-165 to support IModule's interface, which is a requirement for module installation.
abstract contract ModuleBase is ERC165, IModule {
error NotImplemented();
error UnexpectedDataPassed();
modifier assertNoData(bytes calldata data) {
if (data.length > 0) {
revert UnexpectedDataPassed();
}
_;
}
/// @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.
///
/// Supporting the IModule interface is a requirement for module installation. This is also used
/// by the modular account to prevent standard execution functions `execute` and `executeBatch` from
/// making calls to modules.
/// @param interfaceId The interface ID to check for support.
/// @return True if the contract supports `interfaceId`.
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IModule).interfaceId || super.supportsInterface(interfaceId);
}
/// @dev help method that returns extracted selector and calldata. If selector is executeUserOp, return the
/// selector and calldata of the inner call.
function _getSelectorAndCalldata(bytes calldata data) internal pure returns (bytes4, bytes memory) {
bytes4 selector = bytes4(data[:4]);
if (selector == IAccountExecute.executeUserOp.selector) {
(PackedUserOperation memory uo,) = abi.decode(data[4:], (PackedUserOperation, bytes32));
bytes memory finalCalldata = uo.callData;
// Bytes arr representation: [bytes32(len), bytes4(executeUserOp.selector), bytes4(actualSelector),
// bytes(actualCallData)]
assembly ("memory-safe") {
// Copy actualSelector into a new var
selector := shl(224, mload(add(finalCalldata, 8)))
let len := mload(finalCalldata)
// Move the finalCalldata pointer by 8
finalCalldata := add(finalCalldata, 8)
// Shorten bytes arry by 8 by: store length - 8 into the new pointer location
mstore(finalCalldata, sub(len, 8))
}
return (selector, finalCalldata);
}
return (selector, data[4:]);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
// A base for modules that use EIP-712 structured data signing.
//
// Unlike other EIP712 libraries, this mixin uses the salt field to hold the account address.
//
// It omits the name and version from the EIP-712 domain, as modules are intended to be deployed as
// immutable singletons, thus a different versions and instances would have a different module address.
//
// Due to depending on the account address to calculate the domain separator, this abstract contract does not
// implement EIP-5267, as the domain retrieval function does not provide a parameter to use for the account address
// (internally the verifyingContract), and the computed `msg.sender` for an `eth_call` without an override is
// address(0).
abstract contract ModuleEIP712 {
// keccak256(
// "EIP712Domain(uint256 chainId,address verifyingContract,bytes32 salt)"
// )
bytes32 private constant _DOMAIN_SEPARATOR_TYPEHASH =
0x71062c282d40422f744945d587dbf4ecfd4f9cfad1d35d62c944373009d96162;
function _domainSeparator(address account) internal view returns (bytes32) {
return keccak256(
abi.encode(
_DOMAIN_SEPARATOR_TYPEHASH, block.chainid, address(this), bytes32(uint256(uint160(account)))
)
);
}
}// 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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
interface IAccountExecute {
/**
* Account may implement this execute method.
* passing this methodSig at the beginning of callData will cause the entryPoint to pass the full UserOp (and hash)
* to the account.
* The account should skip the methodSig, and use the callData (and optionally, other UserOp fields)
*
* @param userOp - The operation that was just validated.
* @param userOpHash - Hash of the user's request data.
*/
function executeUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}{
"viaIR": true,
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"runs": 10000000,
"enabled": true
},
"evmVersion": "paris",
"remappings": [
":@alchemy/light-account/=lib/light-account/",
":@erc6900/reference-implementation/=node_modules/@erc6900/reference-implementation/src/",
":@eth-infinitism/account-abstraction/=node_modules/account-abstraction/contracts/",
":@openzeppelin/=lib/openzeppelin-contracts/",
":FreshCryptoLib/=lib/webauthn-sol/lib/FreshCryptoLib/solidity/src/",
":account-abstraction/=node_modules/account-abstraction/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-gas-snapshot/=lib/forge-gas-snapshot/src/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/webauthn-sol/lib/openzeppelin-contracts/",
":solady/=node_modules/solady/src/",
":webauthn-sol/=lib/webauthn-sol/"
],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"InvalidSignatureType","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"NotImplemented","type":"error"},{"inputs":[],"name":"UnexpectedDataPassed","type":"error"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint32","name":"entityId","type":"uint32"},{"indexed":true,"internalType":"address","name":"newSigner","type":"address"},{"indexed":false,"internalType":"address","name":"previousSigner","type":"address"}],"name":"SignerTransferred","type":"event"},{"inputs":[],"name":"moduleId","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onInstall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onUninstall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"replaySafeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"address","name":"account","type":"address"}],"name":"signers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"address","name":"newSigner","type":"address"}],"name":"transferSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"validateRuntime","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"digest","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"validateSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"entityId","type":"uint32"},{"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":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60808060405234601557610f88908161001b8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714610646575080630ab8785f146105bb578063217178fb14610525578063219336d6146104d8578063465d33e0146103ca5780634aeb3b03146103825780636d61fe70146102ee5780638a91b0e3146101e0578063a1308f27146101385763e7db7f7e1461008d57600080fd5b346101335760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576100c461078f565b6100cc610748565b906100d56107b2565b506084359067ffffffffffffffff8211610133576020926100fd6101099336906004016107d5565b92909160643591610ad9565b7fffffffff0000000000000000000000000000000000000000000000000000000060405191168152f35b600080fd5b346101335760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576101dc604051610178606082610a2f565b602d81527f616c6368656d792e73696e676c652d7369676e65722d76616c69646174696f6e60208201527f2d6d6f64756c652e312e302e30000000000000000000000000000000000000006040820152604051918291602083526020830190610803565b0390f35b346101335760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335760043567ffffffffffffffff81116101335761023160209136906004016107d5565b90809291810103126101335763ffffffff61024d60009261075b565b16808252816020526040822073ffffffffffffffffffffffffffffffffffffffff3316835260205273ffffffffffffffffffffffffffffffffffffffff604083205416818352826020526040832073ffffffffffffffffffffffffffffffffffffffff33168452602052604083207fffffffffffffffffffffffff0000000000000000000000000000000000000000815416905560405190815260203391a3005b346101335760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335760043567ffffffffffffffff81116101335761033f60409136906004016107d5565b90809291810103126101335760206103568261075b565b91013573ffffffffffffffffffffffffffffffffffffffff81168091036101335761038091610e2f565b005b346101335760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576103806103bc610735565b6103c461076c565b90610e2f565b346101335760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335761040161078f565b610409610748565b906104126107b2565b9160843567ffffffffffffffff8111610133576104339036906004016107d5565b505060a4359067ffffffffffffffff82116101335761045b63ffffffff9236906004016107d5565b505016600052600060205273ffffffffffffffffffffffffffffffffffffffff6040600020911660005260205273ffffffffffffffffffffffffffffffffffffffff80604060002054169116036104ae57005b7fea8e4eb50000000000000000000000000000000000000000000000000000000060005260046000fd5b346101335760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013357602061051d61051461078f565b60243590610965565b604051908152f35b346101335760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335761055c610735565b63ffffffff61056961076c565b9116600052600060205273ffffffffffffffffffffffffffffffffffffffff60406000209116600052602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346101335760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576105f2610735565b6024359067ffffffffffffffff8211610133576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83360301126101335760209161051d916044359160040190610862565b346101335760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013357600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361013357817fab3e34c100000000000000000000000000000000000000000000000000000000602093149081156106d8575b5015158152f35b7f46c0c1b40000000000000000000000000000000000000000000000000000000081149150811561070b575b50836106d1565b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610704565b6004359063ffffffff8216820361013357565b6024359063ffffffff8216820361013357565b359063ffffffff8216820361013357565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361013357565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361013357565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361013357565b9181601f840112156101335782359167ffffffffffffffff8311610133576020838186019501011161013357565b919082519283825260005b84811061084d5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b8060208092840101518282860101520161080e565b63ffffffff1660005260006020526040600020813573ffffffffffffffffffffffffffffffffffffffff811681036101335773ffffffffffffffffffffffffffffffffffffffff1660005260205273ffffffffffffffffffffffffffffffffffffffff60406000205416917f19457468657265756d205369676e6564204d6573736167653a0a333200000000600052601c52603c60002090610100810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561013357019081359167ffffffffffffffff8311610133576020019082360382136101335761095793610bbd565b61096057600190565b600090565b9060429160405173ffffffffffffffffffffffffffffffffffffffff60208201927f71062c282d40422f744945d587dbf4ecfd4f9cfad1d35d62c944373009d961628452466040840152306060840152166080820152608081526109ca60a082610a2f565b519020907f294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff6000526020526040600020604051917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522090565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a7057604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8111610a7057601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b63ffffffff610aed610b3a96959483610965565b9216600052600060205273ffffffffffffffffffffffffffffffffffffffff6040600020911660005260205273ffffffffffffffffffffffffffffffffffffffff60406000205416610bbd565b610b62577fffffffff0000000000000000000000000000000000000000000000000000000090565b7f1626ba7e0000000000000000000000000000000000000000000000000000000090565b929192610b9282610a9f565b91610ba06040519384610a2f565b829481845281830111610133578281602093846000960137010152565b929160018310610e0557803560f81c6002811015610c645780610c9357508260011161013357610c1e92610c189160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3693019101610b86565b90610e9d565b506004811015610c6457159182610c40575b5050610c3b57600090565b600190565b73ffffffffffffffffffffffffffffffffffffffff91925081169116143880610c30565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b90939190600114610cc8577f60cd402d0000000000000000000000000000000000000000000000000000000060005260046000fd5b8260011161013357610d50610d7c610d0c60009695879660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3693019101610b86565b60405192839160208301957f1626ba7e0000000000000000000000000000000000000000000000000000000087526024840152604060448401526064830190610803565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610a2f565b51915afa3d15610dfe573d610d9081610a9f565b90610d9e6040519283610a2f565b81523d6000602083013e5b81610df0575b81610db8575090565b905060208180518101031261013357602001517f1626ba7e000000000000000000000000000000000000000000000000000000001490565b905060208151101590610daf565b6060610da9565b7f60cd402d0000000000000000000000000000000000000000000000000000000060005260046000fd5b63ffffffff166000818152602081815260408083203380855290835292819020805473ffffffffffffffffffffffffffffffffffffffff9687167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255915195909116855293a3565b8151919060418303610ece57610ec792506020820151906060604084015193015160001a90610ed9565b9192909190565b505060009160029190565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411610f6f579160209360809260ff60009560405194855216868401526040830152606082015282805260015afa15610f635760005173ffffffffffffffffffffffffffffffffffffffff811615610f575790600090600090565b50600090600190600090565b6040513d6000823e3d90fd5b5050506000916003919056fea164736f6c634300081a000a
Deployed Bytecode
0x608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714610646575080630ab8785f146105bb578063217178fb14610525578063219336d6146104d8578063465d33e0146103ca5780634aeb3b03146103825780636d61fe70146102ee5780638a91b0e3146101e0578063a1308f27146101385763e7db7f7e1461008d57600080fd5b346101335760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576100c461078f565b6100cc610748565b906100d56107b2565b506084359067ffffffffffffffff8211610133576020926100fd6101099336906004016107d5565b92909160643591610ad9565b7fffffffff0000000000000000000000000000000000000000000000000000000060405191168152f35b600080fd5b346101335760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576101dc604051610178606082610a2f565b602d81527f616c6368656d792e73696e676c652d7369676e65722d76616c69646174696f6e60208201527f2d6d6f64756c652e312e302e30000000000000000000000000000000000000006040820152604051918291602083526020830190610803565b0390f35b346101335760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335760043567ffffffffffffffff81116101335761023160209136906004016107d5565b90809291810103126101335763ffffffff61024d60009261075b565b16808252816020526040822073ffffffffffffffffffffffffffffffffffffffff3316835260205273ffffffffffffffffffffffffffffffffffffffff604083205416818352826020526040832073ffffffffffffffffffffffffffffffffffffffff33168452602052604083207fffffffffffffffffffffffff0000000000000000000000000000000000000000815416905560405190815260203391a3005b346101335760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335760043567ffffffffffffffff81116101335761033f60409136906004016107d5565b90809291810103126101335760206103568261075b565b91013573ffffffffffffffffffffffffffffffffffffffff81168091036101335761038091610e2f565b005b346101335760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576103806103bc610735565b6103c461076c565b90610e2f565b346101335760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335761040161078f565b610409610748565b906104126107b2565b9160843567ffffffffffffffff8111610133576104339036906004016107d5565b505060a4359067ffffffffffffffff82116101335761045b63ffffffff9236906004016107d5565b505016600052600060205273ffffffffffffffffffffffffffffffffffffffff6040600020911660005260205273ffffffffffffffffffffffffffffffffffffffff80604060002054169116036104ae57005b7fea8e4eb50000000000000000000000000000000000000000000000000000000060005260046000fd5b346101335760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013357602061051d61051461078f565b60243590610965565b604051908152f35b346101335760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101335761055c610735565b63ffffffff61056961076c565b9116600052600060205273ffffffffffffffffffffffffffffffffffffffff60406000209116600052602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346101335760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610133576105f2610735565b6024359067ffffffffffffffff8211610133576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83360301126101335760209161051d916044359160040190610862565b346101335760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261013357600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361013357817fab3e34c100000000000000000000000000000000000000000000000000000000602093149081156106d8575b5015158152f35b7f46c0c1b40000000000000000000000000000000000000000000000000000000081149150811561070b575b50836106d1565b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610704565b6004359063ffffffff8216820361013357565b6024359063ffffffff8216820361013357565b359063ffffffff8216820361013357565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361013357565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361013357565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361013357565b9181601f840112156101335782359167ffffffffffffffff8311610133576020838186019501011161013357565b919082519283825260005b84811061084d5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b8060208092840101518282860101520161080e565b63ffffffff1660005260006020526040600020813573ffffffffffffffffffffffffffffffffffffffff811681036101335773ffffffffffffffffffffffffffffffffffffffff1660005260205273ffffffffffffffffffffffffffffffffffffffff60406000205416917f19457468657265756d205369676e6564204d6573736167653a0a333200000000600052601c52603c60002090610100810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561013357019081359167ffffffffffffffff8311610133576020019082360382136101335761095793610bbd565b61096057600190565b600090565b9060429160405173ffffffffffffffffffffffffffffffffffffffff60208201927f71062c282d40422f744945d587dbf4ecfd4f9cfad1d35d62c944373009d961628452466040840152306060840152166080820152608081526109ca60a082610a2f565b519020907f294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff6000526020526040600020604051917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522090565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a7057604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8111610a7057601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b63ffffffff610aed610b3a96959483610965565b9216600052600060205273ffffffffffffffffffffffffffffffffffffffff6040600020911660005260205273ffffffffffffffffffffffffffffffffffffffff60406000205416610bbd565b610b62577fffffffff0000000000000000000000000000000000000000000000000000000090565b7f1626ba7e0000000000000000000000000000000000000000000000000000000090565b929192610b9282610a9f565b91610ba06040519384610a2f565b829481845281830111610133578281602093846000960137010152565b929160018310610e0557803560f81c6002811015610c645780610c9357508260011161013357610c1e92610c189160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3693019101610b86565b90610e9d565b506004811015610c6457159182610c40575b5050610c3b57600090565b600190565b73ffffffffffffffffffffffffffffffffffffffff91925081169116143880610c30565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b90939190600114610cc8577f60cd402d0000000000000000000000000000000000000000000000000000000060005260046000fd5b8260011161013357610d50610d7c610d0c60009695879660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3693019101610b86565b60405192839160208301957f1626ba7e0000000000000000000000000000000000000000000000000000000087526024840152604060448401526064830190610803565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610a2f565b51915afa3d15610dfe573d610d9081610a9f565b90610d9e6040519283610a2f565b81523d6000602083013e5b81610df0575b81610db8575090565b905060208180518101031261013357602001517f1626ba7e000000000000000000000000000000000000000000000000000000001490565b905060208151101590610daf565b6060610da9565b7f60cd402d0000000000000000000000000000000000000000000000000000000060005260046000fd5b63ffffffff166000818152602081815260408083203380855290835292819020805473ffffffffffffffffffffffffffffffffffffffff9687167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255915195909116855293a3565b8151919060418303610ece57610ec792506020820151906060604084015193015160001a90610ed9565b9192909190565b505060009160029190565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411610f6f579160209360809260ff60009560405194855216868401526040830152606082015282805260015afa15610f635760005173ffffffffffffffffffffffffffffffffffffffff811615610f575790600090600090565b50600090600190600090565b6040513d6000823e3d90fd5b5050506000916003919056fea164736f6c634300081a000a
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
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.