Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
21355656 | 90 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
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.