dexorder
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
/**
|
||||
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
|
||||
*
|
||||
* These functions can be used to verify that a message was signed by the holder
|
||||
* of the private keys of a given address.
|
||||
*/
|
||||
library ECDSA {
|
||||
enum RecoverError {
|
||||
NoError,
|
||||
InvalidSignature,
|
||||
InvalidSignatureLength,
|
||||
InvalidSignatureS
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev The signature derives the `address(0)`.
|
||||
*/
|
||||
error ECDSAInvalidSignature();
|
||||
|
||||
/**
|
||||
* @dev The signature has an invalid length.
|
||||
*/
|
||||
error ECDSAInvalidSignatureLength(uint256 length);
|
||||
|
||||
/**
|
||||
* @dev The signature has an S value that is in the upper half order.
|
||||
*/
|
||||
error ECDSAInvalidSignatureS(bytes32 s);
|
||||
|
||||
/**
|
||||
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
|
||||
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
|
||||
* and a bytes32 providing additional information about the error.
|
||||
*
|
||||
* If no error is returned, then the address can be used for verification purposes.
|
||||
*
|
||||
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
|
||||
* this function rejects them by requiring the `s` value to be in the lower
|
||||
* half order, and the `v` value to be either 27 or 28.
|
||||
*
|
||||
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
|
||||
* verification to be secure: it is possible to craft signatures that
|
||||
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
|
||||
* this is by receiving a hash of the original message (which may otherwise
|
||||
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
|
||||
*
|
||||
* Documentation for signature generation:
|
||||
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
|
||||
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
|
||||
*/
|
||||
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
|
||||
if (signature.length == 65) {
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
uint8 v;
|
||||
// ecrecover takes the signature parameters, and the only way to get them
|
||||
// currently is to use assembly.
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
r := mload(add(signature, 0x20))
|
||||
s := mload(add(signature, 0x40))
|
||||
v := byte(0, mload(add(signature, 0x60)))
|
||||
}
|
||||
return tryRecover(hash, v, r, s);
|
||||
} else {
|
||||
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address that signed a hashed message (`hash`) with
|
||||
* `signature`. This address can then be used for verification purposes.
|
||||
*
|
||||
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
|
||||
* this function rejects them by requiring the `s` value to be in the lower
|
||||
* half order, and the `v` value to be either 27 or 28.
|
||||
*
|
||||
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
|
||||
* verification to be secure: it is possible to craft signatures that
|
||||
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
|
||||
* this is by receiving a hash of the original message (which may otherwise
|
||||
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
|
||||
*/
|
||||
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
|
||||
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
|
||||
_throwError(error, errorArg);
|
||||
return recovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
|
||||
*
|
||||
* See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
|
||||
*/
|
||||
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
|
||||
unchecked {
|
||||
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
|
||||
// We do not check for an overflow here since the shift operation results in 0 or 1.
|
||||
uint8 v = uint8((uint256(vs) >> 255) + 27);
|
||||
return tryRecover(hash, v, r, s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
|
||||
*/
|
||||
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
|
||||
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
|
||||
_throwError(error, errorArg);
|
||||
return recovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
|
||||
* `r` and `s` signature fields separately.
|
||||
*/
|
||||
function tryRecover(
|
||||
bytes32 hash,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) internal pure returns (address, RecoverError, bytes32) {
|
||||
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
|
||||
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
|
||||
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
|
||||
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
|
||||
//
|
||||
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
|
||||
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
|
||||
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
|
||||
// these malleable signatures as well.
|
||||
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
|
||||
return (address(0), RecoverError.InvalidSignatureS, s);
|
||||
}
|
||||
|
||||
// If the signature is valid (and not malleable), return the signer address
|
||||
address signer = ecrecover(hash, v, r, s);
|
||||
if (signer == address(0)) {
|
||||
return (address(0), RecoverError.InvalidSignature, bytes32(0));
|
||||
}
|
||||
|
||||
return (signer, RecoverError.NoError, bytes32(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Overload of {ECDSA-recover} that receives the `v`,
|
||||
* `r` and `s` signature fields separately.
|
||||
*/
|
||||
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
|
||||
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
|
||||
_throwError(error, errorArg);
|
||||
return recovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
|
||||
*/
|
||||
function _throwError(RecoverError error, bytes32 errorArg) private pure {
|
||||
if (error == RecoverError.NoError) {
|
||||
return; // no error: do nothing
|
||||
} else if (error == RecoverError.InvalidSignature) {
|
||||
revert ECDSAInvalidSignature();
|
||||
} else if (error == RecoverError.InvalidSignatureLength) {
|
||||
revert ECDSAInvalidSignatureLength(uint256(errorArg));
|
||||
} else if (error == RecoverError.InvalidSignatureS) {
|
||||
revert ECDSAInvalidSignatureS(errorArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {MessageHashUtils} from "./MessageHashUtils.sol";
|
||||
import {ShortStrings, ShortString} from "../ShortStrings.sol";
|
||||
import {IERC5267} from "../../interfaces/IERC5267.sol";
|
||||
|
||||
/**
|
||||
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP-712] is a standard for hashing and signing of typed structured data.
|
||||
*
|
||||
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
|
||||
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
|
||||
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
|
||||
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
|
||||
*
|
||||
* This contract implements the EIP-712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
|
||||
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
|
||||
* ({_hashTypedDataV4}).
|
||||
*
|
||||
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
|
||||
* the chain id to protect against replay attacks on an eventual fork of the chain.
|
||||
*
|
||||
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
|
||||
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
|
||||
*
|
||||
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
|
||||
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
|
||||
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
|
||||
*
|
||||
* @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
||||
*/
|
||||
abstract contract EIP712 is IERC5267 {
|
||||
using ShortStrings for *;
|
||||
|
||||
bytes32 private constant TYPE_HASH =
|
||||
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
|
||||
|
||||
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
|
||||
// invalidate the cached domain separator if the chain id changes.
|
||||
bytes32 private immutable _cachedDomainSeparator;
|
||||
uint256 private immutable _cachedChainId;
|
||||
address private immutable _cachedThis;
|
||||
|
||||
bytes32 private immutable _hashedName;
|
||||
bytes32 private immutable _hashedVersion;
|
||||
|
||||
ShortString private immutable _name;
|
||||
ShortString private immutable _version;
|
||||
string private _nameFallback;
|
||||
string private _versionFallback;
|
||||
|
||||
/**
|
||||
* @dev Initializes the domain separator and parameter caches.
|
||||
*
|
||||
* The meaning of `name` and `version` is specified in
|
||||
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP-712]:
|
||||
*
|
||||
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
|
||||
* - `version`: the current major version of the signing domain.
|
||||
*
|
||||
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
|
||||
* contract upgrade].
|
||||
*/
|
||||
constructor(string memory name, string memory version) {
|
||||
_name = name.toShortStringWithFallback(_nameFallback);
|
||||
_version = version.toShortStringWithFallback(_versionFallback);
|
||||
_hashedName = keccak256(bytes(name));
|
||||
_hashedVersion = keccak256(bytes(version));
|
||||
|
||||
_cachedChainId = block.chainid;
|
||||
_cachedDomainSeparator = _buildDomainSeparator();
|
||||
_cachedThis = address(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the domain separator for the current chain.
|
||||
*/
|
||||
function _domainSeparatorV4() internal view returns (bytes32) {
|
||||
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
|
||||
return _cachedDomainSeparator;
|
||||
} else {
|
||||
return _buildDomainSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
function _buildDomainSeparator() private view returns (bytes32) {
|
||||
return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
|
||||
* function returns the hash of the fully encoded EIP712 message for this domain.
|
||||
*
|
||||
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
|
||||
*
|
||||
* ```solidity
|
||||
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
|
||||
* keccak256("Mail(address to,string contents)"),
|
||||
* mailTo,
|
||||
* keccak256(bytes(mailContents))
|
||||
* )));
|
||||
* address signer = ECDSA.recover(digest, signature);
|
||||
* ```
|
||||
*/
|
||||
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
|
||||
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC-5267}.
|
||||
*/
|
||||
function eip712Domain()
|
||||
public
|
||||
view
|
||||
virtual
|
||||
returns (
|
||||
bytes1 fields,
|
||||
string memory name,
|
||||
string memory version,
|
||||
uint256 chainId,
|
||||
address verifyingContract,
|
||||
bytes32 salt,
|
||||
uint256[] memory extensions
|
||||
)
|
||||
{
|
||||
return (
|
||||
hex"0f", // 01111
|
||||
_EIP712Name(),
|
||||
_EIP712Version(),
|
||||
block.chainid,
|
||||
address(this),
|
||||
bytes32(0),
|
||||
new uint256[](0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev The name parameter for the EIP712 domain.
|
||||
*
|
||||
* NOTE: By default this function reads _name which is an immutable value.
|
||||
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
|
||||
*/
|
||||
// solhint-disable-next-line func-name-mixedcase
|
||||
function _EIP712Name() internal view returns (string memory) {
|
||||
return _name.toStringWithFallback(_nameFallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev The version parameter for the EIP712 domain.
|
||||
*
|
||||
* NOTE: By default this function reads _version which is an immutable value.
|
||||
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
|
||||
*/
|
||||
// solhint-disable-next-line func-name-mixedcase
|
||||
function _EIP712Version() internal view returns (string memory) {
|
||||
return _version.toStringWithFallback(_versionFallback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @dev Library of standard hash functions.
|
||||
*/
|
||||
library Hashes {
|
||||
/**
|
||||
* @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
|
||||
*
|
||||
* NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
|
||||
*/
|
||||
function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
|
||||
return a < b ? _efficientKeccak256(a, b) : _efficientKeccak256(b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
|
||||
*/
|
||||
function _efficientKeccak256(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
mstore(0x00, a)
|
||||
mstore(0x20, b)
|
||||
value := keccak256(0x00, 0x40)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Hashes} from "./Hashes.sol";
|
||||
|
||||
/**
|
||||
* @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 = Hashes.commutativeKeccak256(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 = Hashes.commutativeKeccak256(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] = Hashes.commutativeKeccak256(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] = Hashes.commutativeKeccak256(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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
|
||||
* specifications.
|
||||
*/
|
||||
library MessageHashUtils {
|
||||
/**
|
||||
* @dev Returns the keccak256 digest of an ERC-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 ERC-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 ERC-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 (ERC-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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {ECDSA} from "./ECDSA.sol";
|
||||
import {IERC1271} from "../../interfaces/IERC1271.sol";
|
||||
|
||||
/**
|
||||
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
|
||||
* signatures from externally owned accounts (EOAs) as well as ERC-1271 signatures from smart contract wallets like
|
||||
* Argent and Safe Wallet (previously Gnosis Safe).
|
||||
*/
|
||||
library SignatureChecker {
|
||||
/**
|
||||
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
|
||||
* signature is validated against that smart contract using ERC-1271, otherwise it's validated using `ECDSA.recover`.
|
||||
*
|
||||
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
|
||||
* change through time. It could return true at block N and false at block N+1 (or the opposite).
|
||||
*/
|
||||
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
|
||||
if (signer.code.length == 0) {
|
||||
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
|
||||
return err == ECDSA.RecoverError.NoError && recovered == signer;
|
||||
} else {
|
||||
return isValidERC1271SignatureNow(signer, hash, signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
|
||||
* against the signer smart contract using ERC-1271.
|
||||
*
|
||||
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
|
||||
* change through time. It could return true at block N and false at block N+1 (or the opposite).
|
||||
*/
|
||||
function isValidERC1271SignatureNow(
|
||||
address signer,
|
||||
bytes32 hash,
|
||||
bytes memory signature
|
||||
) internal view returns (bool) {
|
||||
(bool success, bytes memory result) = signer.staticcall(
|
||||
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
|
||||
);
|
||||
return (success &&
|
||||
result.length >= 32 &&
|
||||
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user