Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 21355656 | 392 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
WebAuthnValidator
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {WebAuthn} from "./webauthn/WebAuthn.sol";
import {AbstractValidator} from "./AbstractValidator.sol";
/**
* @title WebAuthnValidator
* @notice This validator validates WebAuthn signatures.
*/
contract WebAuthnValidator is AbstractValidator {
/**
* @notice Verify a signature.
*/
function _verifySignature(bytes memory /*context*/, bytes32 hash, bytes memory signature) override internal view returns (bool valid) {
// decode the signature
(bytes memory authenticatorData, string memory clientDataJSON, uint256 responseTypeLocation, uint256 r, uint256 s, bool usePrecompiled) = abi
.decode(signature, (bytes, string, uint256, uint256, uint256, bool));
// get the public key from storage
(uint256 pubKeyX, uint256 pubKeyY) = abi.decode(validatorStorage[msg.sender], (uint256, uint256));
// The location of the challenge in the clientDataJSON
uint256 CHALLENGE_LOCATION = 23;
// verify the signature using the signature and the public key
valid = WebAuthn.verifySignature(
abi.encodePacked(hash),
authenticatorData,
true,
clientDataJSON,
CHALLENGE_LOCATION,
responseTypeLocation,
r,
s,
pubKeyX,
pubKeyY,
usePrecompiled
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// Loads the table into memory
string memory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
// and split into 4 numbers of 6 bits.
// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
string memory result = new string(4 * ((data.length + 2) / 3));
/// @solidity memory-safe-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 0x20)
let dataPtr := data
let endPtr := add(data, mload(data))
// In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
// set it to zero to make sure no dirty bytes are read in that section.
let afterPtr := add(endPtr, 0x20)
let afterCache := mload(afterPtr)
mstore(afterPtr, 0x00)
// Run over the input, 3 bytes at a time
for {
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 byte (24 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F to bitmask the least significant 6 bits.
// Use this as an index into the lookup table, mload an entire word
// so the desired character is in the least significant byte, and
// mstore8 this least significant byte into the result and continue.
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// Reset the value that was cached
mstore(afterPtr, afterCache)
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// 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: GPL-3.0
pragma solidity ^0.8.24;
enum SigType {
OneOp,
MultiChainSource,
MultiChainTarget,
MultiChainMerkle
}
library AuthAccountLib {
/**
* @notice Parse an account signature into its type and data.
*/
function parseAccountSig(bytes calldata accountSig) internal pure returns (SigType sigType, bytes calldata sigData) {
sigType = SigType(uint8(bytes1(accountSig[:1])));
sigData = accountSig[1:];
}
/**
* @notice Parse a multi-chain signature into rootSig and opHash.
*/
function parseMultiChainSig(bytes calldata sig) internal pure returns (bytes calldata rootSig, bytes32 opHash) {
uint256 opHashOffset = sig.length - 32;
uint256 finalOffset = opHashOffset + 32;
rootSig = sig[:opHashOffset];
opHash = bytes32(sig[opHashOffset:finalOffset]);
}
function parseMerkleTreeSig(bytes calldata sig) internal pure returns (bytes memory rootSig, bytes32 merkleRoot, bytes32[] memory merkleProof) {
return abi.decode(sig, (bytes, bytes32, bytes32[]));
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
library AuthPaymasterLib {
/**
* Parses `sig` into `accountSig` and `paymasterSigData`.
*
* `sig` format:
* |AccountSigLength:uint16|
* |AccountSig:{AccountSigLength}bytes|
* |PaymasterSigData:bytes|
*/
function parseUserOpSignature(bytes calldata sig) internal pure returns (bytes calldata accountSig, bytes calldata paymasterSigData) {
// Parse accountSig length.
uint256 uint16Length = 2;
uint16 accountSigLength = (uint16(uint8(sig[0])) << 8) | uint16(uint8(sig[1]));
// Compute data field offsets.
uint256 accountSigOffset = uint16Length;
uint256 paymasterDataOffset = accountSigOffset + accountSigLength;
accountSig = sig[accountSigOffset:paymasterDataOffset];
paymasterSigData = sig[paymasterDataOffset:];
}
/**
* Parses `paymasterSigData` into `paymasterSig` and `guarantorSig`.
*
* `paymasterSigData` format:
* optional {
* |PaymasterSig:{65}bytes|
* optional{ |GuarantorSig:{65}bytes| }
* }
*/
function parsePaymasterSigData(bytes calldata paymasterSigData) internal pure returns (bytes calldata paymasterSig, bytes calldata guarantorSig) {
// Parse paymasterSig.
paymasterSig = paymasterSigData[0:65];
// Parse guarantorSig.
if (paymasterSigData.length > 65) {
guarantorSig = paymasterSigData[65:130];
} else {
guarantorSig = paymasterSigData[0:0];
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IValidator, IHook} from "./interfaces/IERC7579Modules.sol";
import {MODULE_TYPE_VALIDATOR, MODULE_TYPE_HOOK} from "./types/Constants.sol";
import {SIG_VALIDATION_FAILED_UINT, SIG_VALIDATION_SUCCESS_UINT, ERC1271_MAGICVALUE, ERC1271_INVALID} from "./types/Constants.sol";
import {PackedUserOperation} from "./interfaces/PackedUserOperation.sol";
import {WebAuthn} from "./webauthn/WebAuthn.sol";
import {ExtSigVerifier} from "./ExtSigVerifier.sol";
/**
* @title AbstractValidator
* @notice This is an abstract validator supporting multi-chain signatures. It
* can be instantiated using a signature scheme of choice.
*/
abstract contract AbstractValidator is IValidator, ExtSigVerifier {
// Emitted when a validator is registered.
event ValidatorRegistered(address indexed kernel);
// The validator data of a kernel.
mapping(address kernel => bytes validatorData) public validatorStorage;
function onInstall(bytes calldata _data) external payable override {
// Check if the validator is already initialized.
if (_isInitialized(msg.sender)) revert AlreadyInitialized(msg.sender);
// Update the validator data.
validatorStorage[msg.sender] = _data;
// Emit event.
emit ValidatorRegistered(msg.sender);
}
function onUninstall(bytes calldata) external payable override {
if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
delete validatorStorage[msg.sender];
}
function isModuleType(uint256 typeID) external pure override returns (bool) {
return typeID == MODULE_TYPE_VALIDATOR;
}
function isInitialized(address smartAccount) external view override returns (bool) {
return _isInitialized(smartAccount);
}
function _isInitialized(address smartAccount) internal view returns (bool) {
return validatorStorage[smartAccount].length > 0;
}
/**
* @notice Validate a user operation.
*/
function validateUserOp(PackedUserOperation calldata _userOp, bytes32 _userOpHash) external payable override returns (uint256) {
bool valid = _validateUserOp(bytes(""), _userOp.signature, _userOpHash);
return valid ? SIG_VALIDATION_SUCCESS_UINT : SIG_VALIDATION_FAILED_UINT;
}
/**
* @notice Verify a signature with sender for ERC-1271 validation.
*/
function isValidSignatureWithSender(address /*sender*/, bytes32 hash, bytes calldata sig) external view returns (bytes4) {
bool valid = _validateUserOp(bytes(""), sig, hash);
return valid ? ERC1271_MAGICVALUE : ERC1271_INVALID;
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {AuthPaymasterLib} from "../auth/AuthPaymasterLib.sol";
import {AuthAccountLib, SigType} from "../auth/AuthAccountLib.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
abstract contract ExtSigVerifier {
function _validateUserOp(bytes memory context, bytes calldata _signature, bytes32 _userOpHash) internal view returns (bool) {
(bytes calldata accountSig,) = AuthPaymasterLib.parseUserOpSignature(_signature);
(SigType sigType, bytes calldata sigData) = AuthAccountLib.parseAccountSig(accountSig);
if (sigType == SigType.OneOp) {
// Verify standard user op signature.
bytes32 msgHash = MessageHashUtils.toEthSignedMessageHash(_userOpHash);
return _verifySignature(context, msgHash, sigData);
} else if (sigType == SigType.MultiChainSource || sigType == SigType.MultiChainTarget) {
// Verify two user op signature.
// Parse sigData.
(bytes calldata rootSig, bytes32 otherOpHash) = AuthAccountLib.parseMultiChainSig(sigData);
// Compute root hash.
bool isSourceOp = sigType == SigType.MultiChainSource;
(bytes32 sourceOpHash, bytes32 targetOpHash) = isSourceOp ? (_userOpHash, otherOpHash) : (otherOpHash, _userOpHash);
bytes32 rootHash = keccak256(abi.encode(sourceOpHash, targetOpHash));
// Verify signature on root hash.
bytes32 msgHash = MessageHashUtils.toEthSignedMessageHash(rootHash);
return _verifySignature(context, msgHash, rootSig);
} else if (sigType == SigType.MultiChainMerkle) {
(bytes memory rootSig, bytes32 merkleRoot, bytes32[] memory merkleProof) = AuthAccountLib.parseMerkleTreeSig(sigData);
bytes32 leaf = _userOpHash;
bool success = MerkleProof.verify(merkleProof, merkleRoot, leaf);
if (!success) {
revert("Invalid Merkle proof");
}
bytes32 msgHash = MessageHashUtils.toEthSignedMessageHash(merkleRoot);
return _verifySignature(context, msgHash, rootSig);
}
revert("Invalid signature type");
}
function _verifySignature(bytes memory context, bytes32 hash, bytes memory signature) virtual internal view returns (bool valid);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import {PackedUserOperation} from "./PackedUserOperation.sol";
interface IModule {
error AlreadyInitialized(address smartAccount);
error NotInitialized(address smartAccount);
/**
* @dev This function is called by the smart account during installation of the module
* @param data arbitrary data that may be required on the module during `onInstall`
* initialization
*
* MUST revert on error (i.e. if module is already enabled)
*/
function onInstall(bytes calldata data) external payable;
/**
* @dev This function is called by the smart account during uninstallation of the module
* @param data arbitrary data that may be required on the module during `onUninstall`
* de-initialization
*
* MUST revert on error
*/
function onUninstall(bytes calldata data) external payable;
/**
* @dev Returns boolean value if module is a certain type
* @param moduleTypeId the module type ID according the ERC-7579 spec
*
* MUST return true if the module is of the given type and false otherwise
*/
function isModuleType(uint256 moduleTypeId) external view returns (bool);
/**
* @dev Returns if the module was already initialized for a provided smartaccount
*/
function isInitialized(address smartAccount) external view returns (bool);
}
interface IValidator is IModule {
error InvalidTargetAddress(address target);
/**
* @dev Validates a transaction on behalf of the account.
* This function is intended to be called by the MSA during the ERC-4337 validation phase
* Note: solely relying on bytes32 hash and signature is not sufficient for some
* validation implementations (i.e. SessionKeys often need access to userOp.calldata)
* @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata.
* The MSA MUST clean up the userOp before sending it to the validator.
* @param userOpHash The hash of the user operation to be validated
* @return return value according to ERC-4337
*/
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
external
payable
returns (uint256);
/**
* Validator can be used for ERC-1271 validation
*/
function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data)
external
view
returns (bytes4);
}
interface IExecutor is IModule {}
interface IHook is IModule {
function preCheck(address msgSender, uint256 msgValue, bytes calldata msgData)
external
payable
returns (bytes memory hookData);
function postCheck(bytes calldata hookData) external payable;
}
interface IFallback is IModule {}
interface IPolicy is IModule {
function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp) external payable returns (uint256);
function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
external
view
returns (uint256);
}
interface ISigner is IModule {
function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash)
external
payable
returns (uint256);
function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
external
view
returns (bytes4);
}// 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 maxFeePerGas and maxPriorityFeePerGas - Same as EIP-1559 gas parameter.
* @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; //maxPriorityFee and maxFeePerGas;
bytes paymasterAndData;
bytes signature;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {CallType, ExecType, ExecModeSelector} from "./Types.sol";
import {PassFlag, ValidationMode, ValidationType} from "./Types.sol";
import {ValidationData} from "./Types.sol";
// --- ERC7579 calltypes ---
// Default CallType
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
// Batched CallType
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);
// @dev Implementing delegatecall is OPTIONAL!
// implement delegatecall with extreme care.
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
// --- ERC7579 exectypes ---
// @dev default behavior is to revert on failure
// To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
// Since this is value 0x00, no additional encoding is required for simple accounts
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
// @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
// --- ERC7579 mode selector ---
ExecModeSelector constant EXEC_MODE_DEFAULT = ExecModeSelector.wrap(bytes4(0x00000000));
// --- Kernel permission skip flags ---
PassFlag constant SKIP_USEROP = PassFlag.wrap(0x0001);
PassFlag constant SKIP_SIGNATURE = PassFlag.wrap(0x0002);
// --- Kernel validation modes ---
ValidationMode constant VALIDATION_MODE_DEFAULT = ValidationMode.wrap(0x00);
ValidationMode constant VALIDATION_MODE_ENABLE = ValidationMode.wrap(0x01);
ValidationMode constant VALIDATION_MODE_INSTALL = ValidationMode.wrap(0x02);
// --- Kernel validation types ---
ValidationType constant VALIDATION_TYPE_ROOT = ValidationType.wrap(0x00);
ValidationType constant VALIDATION_TYPE_VALIDATOR = ValidationType.wrap(0x01);
ValidationType constant VALIDATION_TYPE_PERMISSION = ValidationType.wrap(0x02);
// --- storage slots ---
// bytes32(uint256(keccak256('kernel.v3.selector')) - 1)
bytes32 constant SELECTOR_MANAGER_STORAGE_SLOT = 0x7c341349a4360fdd5d5bc07e69f325dc6aaea3eb018b3e0ea7e53cc0bb0d6f3b;
// bytes32(uint256(keccak256('kernel.v3.executor')) - 1)
bytes32 constant EXECUTOR_MANAGER_STORAGE_SLOT = 0x1bbee3173dbdc223633258c9f337a0fff8115f206d302bea0ed3eac003b68b86;
// bytes32(uint256(keccak256('kernel.v3.hook')) - 1)
bytes32 constant HOOK_MANAGER_STORAGE_SLOT = 0x4605d5f70bb605094b2e761eccdc27bed9a362d8612792676bf3fb9b12832ffc;
// bytes32(uint256(keccak256('kernel.v3.validation')) - 1)
bytes32 constant VALIDATION_MANAGER_STORAGE_SLOT = 0x7bcaa2ced2a71450ed5a9a1b4848e8e5206dbc3f06011e595f7f55428cc6f84f;
bytes32 constant ERC1967_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// --- Kernel validation nonce incremental size limit ---
uint32 constant MAX_NONCE_INCREMENT_SIZE = 10;
// -- EIP712 type hash ---
bytes32 constant ENABLE_TYPE_HASH = 0xb17ab1224aca0d4255ef8161acaf2ac121b8faa32a4b2258c912cc5f8308c505;
bytes32 constant KERNEL_WRAPPER_TYPE_HASH = 0x1547321c374afde8a591d972a084b071c594c275e36724931ff96c25f2999c83;
// --- ERC constants ---
// ERC4337 constants
uint256 constant SIG_VALIDATION_FAILED_UINT = 1;
uint256 constant SIG_VALIDATION_SUCCESS_UINT = 0;
ValidationData constant SIG_VALIDATION_FAILED = ValidationData.wrap(SIG_VALIDATION_FAILED_UINT);
// ERC-1271 constants
bytes4 constant ERC1271_MAGICVALUE = 0x1626ba7e;
bytes4 constant ERC1271_INVALID = 0xffffffff;
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_POLICY = 5;
uint256 constant MODULE_TYPE_SIGNER = 6;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
// Custom type for improved developer experience
type ExecMode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ExecModeSelector is bytes4;
type ExecModePayload is bytes22;
using {eqModeSelector as ==} for ExecModeSelector global;
using {eqCallType as ==} for CallType global;
using {notEqCallType as !=} for CallType global;
using {eqExecType as ==} for ExecType global;
function eqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
function notEqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) != CallType.unwrap(b);
}
function eqExecType(ExecType a, ExecType b) pure returns (bool) {
return ExecType.unwrap(a) == ExecType.unwrap(b);
}
function eqModeSelector(ExecModeSelector a, ExecModeSelector b) pure returns (bool) {
return ExecModeSelector.unwrap(a) == ExecModeSelector.unwrap(b);
}
type ValidationMode is bytes1;
type ValidationId is bytes21;
type ValidationType is bytes1;
type PermissionId is bytes4;
type PolicyData is bytes22; // 2bytes for flag on skip, 20 bytes for validator address
type PassFlag is bytes2;
using {vModeEqual as ==} for ValidationMode global;
using {vTypeEqual as ==} for ValidationType global;
using {vIdentifierEqual as ==} for ValidationId global;
using {vModeNotEqual as !=} for ValidationMode global;
using {vTypeNotEqual as !=} for ValidationType global;
using {vIdentifierNotEqual as !=} for ValidationId global;
// nonce = uint192(key) + nonce
// key = mode + (vtype + validationDataWithoutType) + 2bytes parallelNonceKey
// key = 0x00 + 0x00 + 0x000 .. 00 + 0x0000
// key = 0x00 + 0x01 + 0x1234...ff + 0x0000
// key = 0x00 + 0x02 + ( ) + 0x000
function vModeEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
return ValidationMode.unwrap(a) == ValidationMode.unwrap(b);
}
function vModeNotEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
return ValidationMode.unwrap(a) != ValidationMode.unwrap(b);
}
function vTypeEqual(ValidationType a, ValidationType b) pure returns (bool) {
return ValidationType.unwrap(a) == ValidationType.unwrap(b);
}
function vTypeNotEqual(ValidationType a, ValidationType b) pure returns (bool) {
return ValidationType.unwrap(a) != ValidationType.unwrap(b);
}
function vIdentifierEqual(ValidationId a, ValidationId b) pure returns (bool) {
return ValidationId.unwrap(a) == ValidationId.unwrap(b);
}
function vIdentifierNotEqual(ValidationId a, ValidationId b) pure returns (bool) {
return ValidationId.unwrap(a) != ValidationId.unwrap(b);
}
type ValidationData is uint256;
type ValidAfter is uint48;
type ValidUntil is uint48;
function getValidationResult(ValidationData validationData) pure returns (address result) {
assembly {
result := validationData
}
}
function packValidationData(ValidAfter validAfter, ValidUntil validUntil) pure returns (uint256) {
return uint256(ValidAfter.unwrap(validAfter)) << 208 | uint256(ValidUntil.unwrap(validUntil)) << 160;
}
function parseValidationData(uint256 validationData)
pure
returns (ValidAfter validAfter, ValidUntil validUntil, address result)
{
assembly {
result := validationData
validUntil := and(shr(160, validationData), 0xffffffffffff)
switch iszero(validUntil)
case 1 { validUntil := 0xffffffffffff }
validAfter := shr(208, validationData)
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Base64.sol";
library Base64URL {
function encode(bytes memory data) internal pure returns (string memory) {
string memory strb64 = Base64.encode(data);
bytes memory b64 = bytes(strb64);
// Base64 can end with "=" or "=="; Base64URL has no padding.
uint256 equalsCount = 0;
if (b64.length > 2 && b64[b64.length - 2] == "=") equalsCount = 2;
else if (b64.length > 1 && b64[b64.length - 1] == "=") equalsCount = 1;
uint256 len = b64.length - equalsCount;
bytes memory result = new bytes(len);
for (uint256 i = 0; i < len; i++) {
if (b64[i] == "+") {
result[i] = "-";
} else if (b64[i] == "/") {
result[i] = "_";
} else {
result[i] = b64[i];
}
}
return string(result);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* Helper library for external contracts to verify P256 signatures.
*
*/
library P256 {
address constant DAIMO_VERIFIER = 0xc2b78104907F722DABAc4C69f826a522B2754De4;
address constant PRECOMPILED_VERIFIER = 0x0000000000000000000000000000000000000100;
function verifySignatureAllowMalleability(
bytes32 message_hash,
uint256 r,
uint256 s,
uint256 x,
uint256 y,
bool usePrecompiled
) internal view returns (bool) {
bytes memory args = abi.encode(message_hash, r, s, x, y);
if (usePrecompiled) {
(bool success, bytes memory ret) = PRECOMPILED_VERIFIER.staticcall(args);
if (success == false || ret.length == 0) {
return false;
}
return abi.decode(ret, (uint256)) == 1;
} else {
(, bytes memory ret) = DAIMO_VERIFIER.staticcall(args);
return abi.decode(ret, (uint256)) == 1;
}
}
/// P256 curve order n/2 for malleability check
uint256 constant P256_N_DIV_2 = 57896044605178124381348723474703786764998477612067880171211129530534256022184;
function verifySignature(bytes32 message_hash, uint256 r, uint256 s, uint256 x, uint256 y, bool usePrecompiled) internal view returns (bool) {
// check for signature malleability
if (s > P256_N_DIV_2) {
return false;
}
return verifySignatureAllowMalleability(message_hash, r, s, x, y, usePrecompiled);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Base64URL.sol";
import "./P256.sol";
/**
* Helper library for external contracts to verify WebAuthn signatures.
*
*/
library WebAuthn {
/// Checks whether substr occurs in str starting at a given byte offset.
function contains(string memory substr, string memory str, uint256 location) internal pure returns (bool) {
bytes memory substrBytes = bytes(substr);
bytes memory strBytes = bytes(str);
uint256 substrLen = substrBytes.length;
uint256 strLen = strBytes.length;
for (uint256 i = 0; i < substrLen; i++) {
if (location + i >= strLen) {
return false;
}
if (substrBytes[i] != strBytes[location + i]) {
return false;
}
}
return true;
}
bytes1 constant AUTH_DATA_FLAGS_UP = 0x01; // Bit 0
bytes1 constant AUTH_DATA_FLAGS_UV = 0x04; // Bit 2
bytes1 constant AUTH_DATA_FLAGS_BE = 0x08; // Bit 3
bytes1 constant AUTH_DATA_FLAGS_BS = 0x10; // Bit 4
/// Verifies the authFlags in authenticatorData. Numbers in inline comment
/// correspond to the same numbered bullets in
/// https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion.
function checkAuthFlags(bytes1 flags, bool requireUserVerification) internal pure returns (bool) {
// 17. Verify that the UP bit of the flags in authData is set.
if (flags & AUTH_DATA_FLAGS_UP != AUTH_DATA_FLAGS_UP) {
return false;
}
// 18. If user verification was determined to be required, verify that
// the UV bit of the flags in authData is set. Otherwise, ignore the
// value of the UV flag.
if (requireUserVerification && (flags & AUTH_DATA_FLAGS_UV) != AUTH_DATA_FLAGS_UV) {
return false;
}
// 19. If the BE bit of the flags in authData is not set, verify that
// the BS bit is not set.
if (flags & AUTH_DATA_FLAGS_BE != AUTH_DATA_FLAGS_BE) {
if (flags & AUTH_DATA_FLAGS_BS == AUTH_DATA_FLAGS_BS) {
return false;
}
}
return true;
}
/**
* Verifies a Webauthn P256 signature (Authentication Assertion) as described
* in https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion. We do not
* verify all the steps as described in the specification, only ones relevant
* to our context. Please carefully read through this list before usage.
* Specifically, we do verify the following:
* - Verify that authenticatorData (which comes from the authenticator,
* such as iCloud Keychain) indicates a well-formed assertion. If
* requireUserVerification is set, checks that the authenticator enforced
* user verification. User verification should be required if,
* and only if, options.userVerification is set to required in the request
* - Verifies that the client JSON is of type "webauthn.get", i.e. the client
* was responding to a request to assert authentication.
* - Verifies that the client JSON contains the requested challenge.
* - Finally, verifies that (r, s) constitute a valid signature over both
* the authenicatorData and client JSON, for public key (x, y).
*
* We make some assumptions about the particular use case of this verifier,
* so we do NOT verify the following:
* - Does NOT verify that the origin in the clientDataJSON matches the
* Relying Party's origin: It is considered the authenticator's
* responsibility to ensure that the user is interacting with the correct
* RP. This is enforced by most high quality authenticators properly,
* particularly the iCloud Keychain and Google Password Manager were
* tested.
* - Does NOT verify That c.topOrigin is well-formed: We assume c.topOrigin
* would never be present, i.e. the credentials are never used in a
* cross-origin/iframe context. The website/app set up should disallow
* cross-origin usage of the credentials. This is the default behaviour for
* created credentials in common settings.
* - Does NOT verify that the rpIdHash in authData is the SHA-256 hash of an
* RP ID expected by the Relying Party: This means that we rely on the
* authenticator to properly enforce credentials to be used only by the
* correct RP. This is generally enforced with features like Apple App Site
* Association and Google Asset Links. To protect from edge cases in which
* a previously-linked RP ID is removed from the authorised RP IDs,
* we recommend that messages signed by the authenticator include some
* expiry mechanism.
* - Does NOT verify the credential backup state: This assumes the credential
* backup state is NOT used as part of Relying Party business logic or
* policy.
* - Does NOT verify the values of the client extension outputs: This assumes
* that the Relying Party does not use client extension outputs.
* - Does NOT verify the signature counter: Signature counters are intended
* to enable risk scoring for the Relying Party. This assumes risk scoring
* is not used as part of Relying Party business logic or policy.
* - Does NOT verify the attestation object: This assumes that
* response.attestationObject is NOT present in the response, i.e. the
* RP does not intend to verify an attestation.
*/
function verifySignature(
bytes memory challenge,
bytes memory authenticatorData,
bool requireUserVerification,
string memory clientDataJSON,
uint256 challengeLocation,
uint256 responseTypeLocation,
uint256 r,
uint256 s,
uint256 x,
uint256 y,
bool usePrecompiled
) internal view returns (bool) {
/// @notice defer the result to the end so dummy signature can go through all verification process including p256.verifySignature
bool deferredResult = true;
// Check that authenticatorData has good flags
if (authenticatorData.length < 37 || !checkAuthFlags(authenticatorData[32], requireUserVerification)) {
deferredResult = false;
}
// Check that response is for an authentication assertion
string memory responseType = '"type":"webauthn.get"';
if (!contains(responseType, clientDataJSON, responseTypeLocation)) {
deferredResult = false;
}
// Check that challenge is in the clientDataJSON
string memory challengeB64url = Base64URL.encode(challenge);
string memory challengeProperty = string.concat('"challenge":"', challengeB64url, '"');
if (!contains(challengeProperty, clientDataJSON, challengeLocation)) {
deferredResult = false;
}
// Check that the public key signed sha256(authenticatorData || sha256(clientDataJSON))
bytes32 clientDataJSONHash = sha256(bytes(clientDataJSON));
bytes32 messageHash = sha256(abi.encodePacked(authenticatorData, clientDataJSONHash));
// if responseTypeLocation is set to max, it means the signature is a dummy signature
if (responseTypeLocation == type(uint256).max) {
return P256.verifySignature(messageHash, r, s, x, y, false);
}
bool verified = P256.verifySignature(messageHash, r, s, x, y, usePrecompiled);
if (verified && deferredResult) {
return true;
}
return false;
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"evmVersion": "cancun",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"InvalidTargetAddress","type":"error"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"NotInitialized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"}],"name":"ValidatorRegistered","type":"event"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"typeID","type":"uint256"}],"name":"isModuleType","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"isValidSignatureWithSender","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onInstall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"onUninstall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"kernel","type":"address"}],"name":"validatorStorage","outputs":[{"internalType":"bytes","name":"validatorData","type":"bytes"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60808060405234610016576113ef908161001b8239f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f3560e01c806322ea7134146103f25780636d61fe701461026b5780638a91b0e3146101ca578063970032031461013e578063d60b347f14610110578063ecd05961146100f05763f551e2ee14610069575f80fd5b346100ec5760603660031901126100ec57610082610456565b50604435916001600160401b0383116100ec576100a76100bd91602094369101610608565b5f84516100b3816104a4565b52602435916106bc565b156100dd57630b135d3f60e11b905b516001600160e01b03199091168152f35b6001600160e01b0319906100cc565b5f80fd5b5090346100ec5760203660031901126100ec576001602092519135148152f35b50346100ec5760203660031901126100ec57602090610135610130610456565b610666565b90519015158152f35b506003199181833601126100ec576001600160401b039281358481116100ec57610120813603928301126100ec575f8451610178816104a4565b5261010481013591602219018212156100ec5701908101359283116100ec576024019180360383136100ec576020926101b491602435916106bc565b156101c2575f905b51908152f35b6001906101bc565b509060203660031901126100ec5780356001600160401b0381116100ec576101f59036908301610608565b505061020033610666565b156102555750335f525f6020525f20610219815461046c565b908161022157005b81601f5f9311600114610232575055005b908083918252610251601f60208420940160051c840160018501610635565b5555005b602491519063f91bd6f160e01b82523390820152fd5b50602091826003193601126100ec576001600160401b039181358381116100ec576102999036908401610608565b9290916102a533610666565b6103db57335f525f86525f209383116103c857506102c3835461046c565b601f811161038f575b505f93601f831160011461032c5750928192935f92610321575b50508160011b915f199060031b1c19161790555b337fd09501348473474a20c772c79c653e1fd7e8b437e418fe235d277d2c888532515f80a2005b013590505f806102e6565b90601f19831694845f52825f20925f905b87821061037757505083600195961061035e575b505050811b0190556102fa565b01355f19600384901b60f8161c191690555f8080610351565b8060018496829495870135815501950192019061033d565b6103b890845f52855f20601f850160051c8101918786106103be575b601f0160051c0190610635565b5f6102cc565b90915081906103ab565b604190634e487b7160e01b5f525260245ffd5b6024925051906393360fbf60e01b82523390820152fd5b50346100ec5760203660031901126100ec576001600160a01b03610414610456565b165f525f602052610426815f20610545565b90805180926020825261044881518092816020860152602086860191016105e7565b601f01601f19168101030190f35b600435906001600160a01b03821682036100ec57565b90600182811c9216801561049a575b602083101461048657565b634e487b7160e01b5f52602260045260245ffd5b91607f169161047b565b602081019081106001600160401b038211176104bf57604052565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b038211176104bf57604052565b606081019081106001600160401b038211176104bf57604052565b60c081019081106001600160401b038211176104bf57604052565b90601f801991011681019081106001600160401b038211176104bf57604052565b9060405191825f82546105578161046c565b908184526020946001916001811690815f146105c55750600114610587575b50505061058592500383610524565b565b5f90815285812095935091905b8183106105ad57505061058593508201015f8080610576565b85548884018501529485019487945091830191610594565b9250505061058594925060ff191682840152151560051b8201015f8080610576565b5f5b8381106105f85750505f910152565b81810151838201526020016105e9565b9181601f840112156100ec578235916001600160401b0383116100ec57602083818601950101116100ec57565b818110610640575050565b5f8155600101610635565b6001600160401b0381116104bf57601f01601f191660200190565b60018060a01b03165f525f60205261068160405f205461046c565b151590565b9291926106928261064b565b916106a06040519384610524565b8294818452818301116100ec578281602093845f960137010152565b91908015610bef5760019181831015610bef578284013560f81c61ff00853560f01c161791826002019081600211610bdb578082116100ec57610700918187610c10565b50508183116100ec57600491600285013560f81c83811015610bc85760038601935f19928381019683155f1461084457505050505061076d9293507f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20923691610686565b80519160209282019060c0838584019303126100ec5783830151926001600160401b03938481116100ec5781019280603f850112156100ec576107b881878601516040809701610c28565b94848301519081116100ec5782019080603f830112156100ec57868201516107e1928601610c28565b606082015160808301519160c060a08501519401519687151588036100ec57335f525f8952610811875f20610545565b9587878051810103126100ec5789886108419b89015198015198519181830152815261083c816104d3565b610c6e565b90565b5f969495969881851494858015610bbb575b1561096e57505050602019019580871161095b5780876108769286610c10565b9790359460209889811061094a575b505061093757501561092e576108e891925b604093845190878201928352858201528481526108b3816104ee565b5190207f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20933691610686565b91825183019260c0818686019503126100ec5784810151936001600160401b03948581116100ec57820181603f820112156100ec57818186896107b89401519101610c28565b6108e891610897565b602190634e487b7160e01b5f525260245ffd5b890360031b1b909416935f80610885565b601182634e487b7160e01b5f525260245ffd5b9293999197969850949350610ba8576003146109c15760405162461bcd60e51b81526020818901526016602482015275496e76616c6964207369676e6174757265207479706560501b6044820152606490fd5b6060908397969701968360028901980301126100ec5735936001600160401b03948581116100ec578201866022820112156100ec57868160236003610a099401359101610686565b926023830135926043810135908782116100ec570194876022870112156100ec57600386013595878711610b95576005928760051b9960209a8b9360409a610a558c5196840187610524565b855260238d8601928201019283116100ec57908c60238a98979695949301915b838310610b825750505050908291935f935b610b35575b5050505003610afb57507f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20908051810160c0828783019203126100ec57858201518581116100ec57820181603f820112156100ec57818186896107b89401519101610c28565b835162461bcd60e51b8152908101869052601460248201527324b73b30b634b21026b2b935b63290383937b7b360611b6044820152606490fd5b909192938251851015610b7c578495508b8495831b84010151908181105f14610b6e575f528b5282895f205b9501929190879594610a87565b905f528b5282895f20610b61565b93610a8c565b823581528a9850918101918e9101610a75565b604184634e487b7160e01b5f525260245ffd5b602188634e487b7160e01b5f525260245ffd5b505f9a5060028114610856565b602184634e487b7160e01b5f525260245ffd5b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b91908201809211610bdb57565b909392938483116100ec5784116100ec578101920390565b90929192610c358161064b565b91610c436040519384610524565b8294828452828201116100ec5760206105859301906105e7565b908151811015610bef570160200190565b979593919096949297600197805160258110908115610f79575b50610f71575b604092835192610c9d846104d3565b60158452610ccb8c8360209674113a3cb832911d113bb2b130baba34371733b2ba1160591b8882015261106c565b15610f69575b610cda90611199565b5f8151600281119081610f39575b5015610eed57506002905b8051918203918211610bdb578290610d0a8361064b565b92610d1788519485610524565b808452610d238161064b565b8488019290601f19013684375f5b828110610e6957505050610d89602e610d8e94610d6f938a519485926c1131b430b63632b733b2911d1160991b8c850152518092602d8501906105e7565b8101601160f91b602d82015203600e810184520182610524565b610ffc565b15610e61575b5f610dac8492838751928284809451938492016105e7565b8101039060025afa15610e5757610e005f918251610df0868051809388610ddc81840197888151938492016105e7565b820190898201520387810184520182610524565b85519283928392519283916105e7565b8101039060025afa15610e4e57505f51965f1914610e3f57610e229596611164565b9081610e37575b50610e32575f90565b600190565b90505f610e29565b919350919350610841946110d1565b513d5f823e3d90fd5b82513d5f823e3d90fd5b5f9950610d94565b92935090916001906001600160f81b0319602b60f81b81610e8a8487610c5d565b511603610eab5750602d610e9e8288610c5d565b535b019085939291610d31565b602f60f81b81610ebb8487610c5d565b511603610ed55750605f610ecf8288610c5d565b53610ea0565b610edf8285610c5d565b51165f1a610ecf8288610c5d565b908051600181119081610f0a575b5015610cf35760019150610cf3565b5f19810191508111610bdb57603d60f81b906001600160f81b031990610f309084610c5d565b5116145f610efb565b600119810191508111610bdb57603d60f81b906001600160f81b031990610f609085610c5d565b5116145f610ce8565b5f9a50610cd1565b5f9850610c8e565b905060201015610bef576040810151610f9b906001600160f81b031916610fa2565b155f610c88565b6001600160f81b0319600160f81b821601610ff757603f60fa1b600160fa1b821601610ff757601f60fb1b600160fb1b821601610fe0575b50600190565b600160fc1b90811614610ff3575f610fda565b5f90565b505f90565b908151918151905f5b848110611016575050505050600190565b6017818101809111610bdb5783811015611062576001600160f81b031961104a816110418587610c5d565b51169287610c5d565b51160361105957600101611005565b50505050505f90565b5050505050505f90565b919082519281515f5b85811061108757505050505050600190565b816110928287610c03565b1015611062576001600160f81b0319806110ac8386610c5d565b5116906110c26110bc8489610c03565b87610c5d565b51160361106257600101611075565b9192937f7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a88411611059575f948594604051936020850195865260408501526060840152608083015260a082015260a0815261112b81610509565b519073c2b78104907f722dabac4c69f826a522b2754de45afa5061114d611313565b6020818051810103126100ec576020015160011490565b94939291907f7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a882116110625761084195611342565b908151156112ff57604051916111ae836104ee565b604083527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208401527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604084015280519260029160028501809511610bdb57600394859004600281901b93906001600160fe1b03811603610bdb579461124e6112388561064b565b946112466040519687610524565b80865261064b565b6020850190601f1901368237928291835184019760208901928351945f85525b8a81106112b2575050505060039394959650525106806001146112a057600214611296575090565b603d905f19015390565b50603d90815f19820153600119015390565b836004919b989b019a8b51600190603f9082828260121c16870101518453828282600c1c16870101518385015382828260061c16870101518785015316840101518582015301969961126e565b905060405161130d816104a4565b5f815290565b3d1561133d573d906113248261064b565b916113326040519384610524565b82523d5f602084013e565b606090565b92935f959192948695604051946020860196875260408601526060850152608084015260a083015260a0825261137782610509565b831461112b5751906101005afa61138c611313565b901580156113b0575b610ff7576020818051810103126100ec576020015160011490565b5080511561139556fea2646970667358221220191125a7587dc583ebfb366c2669d2554a0ffb0308f5a9ab2d5e4e03b5d8cac664736f6c63430008180033
Deployed Bytecode
0x60406080815260049081361015610014575f80fd5b5f3560e01c806322ea7134146103f25780636d61fe701461026b5780638a91b0e3146101ca578063970032031461013e578063d60b347f14610110578063ecd05961146100f05763f551e2ee14610069575f80fd5b346100ec5760603660031901126100ec57610082610456565b50604435916001600160401b0383116100ec576100a76100bd91602094369101610608565b5f84516100b3816104a4565b52602435916106bc565b156100dd57630b135d3f60e11b905b516001600160e01b03199091168152f35b6001600160e01b0319906100cc565b5f80fd5b5090346100ec5760203660031901126100ec576001602092519135148152f35b50346100ec5760203660031901126100ec57602090610135610130610456565b610666565b90519015158152f35b506003199181833601126100ec576001600160401b039281358481116100ec57610120813603928301126100ec575f8451610178816104a4565b5261010481013591602219018212156100ec5701908101359283116100ec576024019180360383136100ec576020926101b491602435916106bc565b156101c2575f905b51908152f35b6001906101bc565b509060203660031901126100ec5780356001600160401b0381116100ec576101f59036908301610608565b505061020033610666565b156102555750335f525f6020525f20610219815461046c565b908161022157005b81601f5f9311600114610232575055005b908083918252610251601f60208420940160051c840160018501610635565b5555005b602491519063f91bd6f160e01b82523390820152fd5b50602091826003193601126100ec576001600160401b039181358381116100ec576102999036908401610608565b9290916102a533610666565b6103db57335f525f86525f209383116103c857506102c3835461046c565b601f811161038f575b505f93601f831160011461032c5750928192935f92610321575b50508160011b915f199060031b1c19161790555b337fd09501348473474a20c772c79c653e1fd7e8b437e418fe235d277d2c888532515f80a2005b013590505f806102e6565b90601f19831694845f52825f20925f905b87821061037757505083600195961061035e575b505050811b0190556102fa565b01355f19600384901b60f8161c191690555f8080610351565b8060018496829495870135815501950192019061033d565b6103b890845f52855f20601f850160051c8101918786106103be575b601f0160051c0190610635565b5f6102cc565b90915081906103ab565b604190634e487b7160e01b5f525260245ffd5b6024925051906393360fbf60e01b82523390820152fd5b50346100ec5760203660031901126100ec576001600160a01b03610414610456565b165f525f602052610426815f20610545565b90805180926020825261044881518092816020860152602086860191016105e7565b601f01601f19168101030190f35b600435906001600160a01b03821682036100ec57565b90600182811c9216801561049a575b602083101461048657565b634e487b7160e01b5f52602260045260245ffd5b91607f169161047b565b602081019081106001600160401b038211176104bf57604052565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b038211176104bf57604052565b606081019081106001600160401b038211176104bf57604052565b60c081019081106001600160401b038211176104bf57604052565b90601f801991011681019081106001600160401b038211176104bf57604052565b9060405191825f82546105578161046c565b908184526020946001916001811690815f146105c55750600114610587575b50505061058592500383610524565b565b5f90815285812095935091905b8183106105ad57505061058593508201015f8080610576565b85548884018501529485019487945091830191610594565b9250505061058594925060ff191682840152151560051b8201015f8080610576565b5f5b8381106105f85750505f910152565b81810151838201526020016105e9565b9181601f840112156100ec578235916001600160401b0383116100ec57602083818601950101116100ec57565b818110610640575050565b5f8155600101610635565b6001600160401b0381116104bf57601f01601f191660200190565b60018060a01b03165f525f60205261068160405f205461046c565b151590565b9291926106928261064b565b916106a06040519384610524565b8294818452818301116100ec578281602093845f960137010152565b91908015610bef5760019181831015610bef578284013560f81c61ff00853560f01c161791826002019081600211610bdb578082116100ec57610700918187610c10565b50508183116100ec57600491600285013560f81c83811015610bc85760038601935f19928381019683155f1461084457505050505061076d9293507f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20923691610686565b80519160209282019060c0838584019303126100ec5783830151926001600160401b03938481116100ec5781019280603f850112156100ec576107b881878601516040809701610c28565b94848301519081116100ec5782019080603f830112156100ec57868201516107e1928601610c28565b606082015160808301519160c060a08501519401519687151588036100ec57335f525f8952610811875f20610545565b9587878051810103126100ec5789886108419b89015198015198519181830152815261083c816104d3565b610c6e565b90565b5f969495969881851494858015610bbb575b1561096e57505050602019019580871161095b5780876108769286610c10565b9790359460209889811061094a575b505061093757501561092e576108e891925b604093845190878201928352858201528481526108b3816104ee565b5190207f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20933691610686565b91825183019260c0818686019503126100ec5784810151936001600160401b03948581116100ec57820181603f820112156100ec57818186896107b89401519101610c28565b6108e891610897565b602190634e487b7160e01b5f525260245ffd5b890360031b1b909416935f80610885565b601182634e487b7160e01b5f525260245ffd5b9293999197969850949350610ba8576003146109c15760405162461bcd60e51b81526020818901526016602482015275496e76616c6964207369676e6174757265207479706560501b6044820152606490fd5b6060908397969701968360028901980301126100ec5735936001600160401b03948581116100ec578201866022820112156100ec57868160236003610a099401359101610686565b926023830135926043810135908782116100ec570194876022870112156100ec57600386013595878711610b95576005928760051b9960209a8b9360409a610a558c5196840187610524565b855260238d8601928201019283116100ec57908c60238a98979695949301915b838310610b825750505050908291935f935b610b35575b5050505003610afb57507f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20908051810160c0828783019203126100ec57858201518581116100ec57820181603f820112156100ec57818186896107b89401519101610c28565b835162461bcd60e51b8152908101869052601460248201527324b73b30b634b21026b2b935b63290383937b7b360611b6044820152606490fd5b909192938251851015610b7c578495508b8495831b84010151908181105f14610b6e575f528b5282895f205b9501929190879594610a87565b905f528b5282895f20610b61565b93610a8c565b823581528a9850918101918e9101610a75565b604184634e487b7160e01b5f525260245ffd5b602188634e487b7160e01b5f525260245ffd5b505f9a5060028114610856565b602184634e487b7160e01b5f525260245ffd5b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b91908201809211610bdb57565b909392938483116100ec5784116100ec578101920390565b90929192610c358161064b565b91610c436040519384610524565b8294828452828201116100ec5760206105859301906105e7565b908151811015610bef570160200190565b979593919096949297600197805160258110908115610f79575b50610f71575b604092835192610c9d846104d3565b60158452610ccb8c8360209674113a3cb832911d113bb2b130baba34371733b2ba1160591b8882015261106c565b15610f69575b610cda90611199565b5f8151600281119081610f39575b5015610eed57506002905b8051918203918211610bdb578290610d0a8361064b565b92610d1788519485610524565b808452610d238161064b565b8488019290601f19013684375f5b828110610e6957505050610d89602e610d8e94610d6f938a519485926c1131b430b63632b733b2911d1160991b8c850152518092602d8501906105e7565b8101601160f91b602d82015203600e810184520182610524565b610ffc565b15610e61575b5f610dac8492838751928284809451938492016105e7565b8101039060025afa15610e5757610e005f918251610df0868051809388610ddc81840197888151938492016105e7565b820190898201520387810184520182610524565b85519283928392519283916105e7565b8101039060025afa15610e4e57505f51965f1914610e3f57610e229596611164565b9081610e37575b50610e32575f90565b600190565b90505f610e29565b919350919350610841946110d1565b513d5f823e3d90fd5b82513d5f823e3d90fd5b5f9950610d94565b92935090916001906001600160f81b0319602b60f81b81610e8a8487610c5d565b511603610eab5750602d610e9e8288610c5d565b535b019085939291610d31565b602f60f81b81610ebb8487610c5d565b511603610ed55750605f610ecf8288610c5d565b53610ea0565b610edf8285610c5d565b51165f1a610ecf8288610c5d565b908051600181119081610f0a575b5015610cf35760019150610cf3565b5f19810191508111610bdb57603d60f81b906001600160f81b031990610f309084610c5d565b5116145f610efb565b600119810191508111610bdb57603d60f81b906001600160f81b031990610f609085610c5d565b5116145f610ce8565b5f9a50610cd1565b5f9850610c8e565b905060201015610bef576040810151610f9b906001600160f81b031916610fa2565b155f610c88565b6001600160f81b0319600160f81b821601610ff757603f60fa1b600160fa1b821601610ff757601f60fb1b600160fb1b821601610fe0575b50600190565b600160fc1b90811614610ff3575f610fda565b5f90565b505f90565b908151918151905f5b848110611016575050505050600190565b6017818101809111610bdb5783811015611062576001600160f81b031961104a816110418587610c5d565b51169287610c5d565b51160361105957600101611005565b50505050505f90565b5050505050505f90565b919082519281515f5b85811061108757505050505050600190565b816110928287610c03565b1015611062576001600160f81b0319806110ac8386610c5d565b5116906110c26110bc8489610c03565b87610c5d565b51160361106257600101611075565b9192937f7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a88411611059575f948594604051936020850195865260408501526060840152608083015260a082015260a0815261112b81610509565b519073c2b78104907f722dabac4c69f826a522b2754de45afa5061114d611313565b6020818051810103126100ec576020015160011490565b94939291907f7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a882116110625761084195611342565b908151156112ff57604051916111ae836104ee565b604083527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208401527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604084015280519260029160028501809511610bdb57600394859004600281901b93906001600160fe1b03811603610bdb579461124e6112388561064b565b946112466040519687610524565b80865261064b565b6020850190601f1901368237928291835184019760208901928351945f85525b8a81106112b2575050505060039394959650525106806001146112a057600214611296575090565b603d905f19015390565b50603d90815f19820153600119015390565b836004919b989b019a8b51600190603f9082828260121c16870101518453828282600c1c16870101518385015382828260061c16870101518785015316840101518582015301969961126e565b905060405161130d816104a4565b5f815290565b3d1561133d573d906113248261064b565b916113326040519384610524565b82523d5f602084013e565b606090565b92935f959192948695604051946020860196875260408601526060850152608084015260a083015260a0825261137782610509565b831461112b5751906101005afa61138c611313565b901580156113b0575b610ff7576020818051810103126100ec576020015160011490565b5080511561139556fea2646970667358221220191125a7587dc583ebfb366c2669d2554a0ffb0308f5a9ab2d5e4e03b5d8cac664736f6c63430008180033
Loading...
Loading
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.