96 lines
3.7 KiB
Solidity
96 lines
3.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity >=0.8.4;
|
|
|
|
/// @notice Gas optimized ECDSA wrapper.
|
|
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
|
|
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
|
|
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
|
|
library ECDSA {
|
|
/// @dev The signature is invalid.
|
|
error InvalidSignature();
|
|
|
|
/// @dev The number which `s` must not exceed in order for
|
|
/// the signature to be non-malleable.
|
|
bytes32 private constant _MALLEABILITY_THRESHOLD =
|
|
0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;
|
|
|
|
/// @dev Recovers the signer's address from a message digest `hash`,
|
|
/// and the `signature`.
|
|
///
|
|
/// This function does NOT accept EIP-2098 short form signatures.
|
|
/// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
|
|
/// short form signatures instead.
|
|
function recoverCalldata(
|
|
bytes32 hash,
|
|
bytes calldata signature
|
|
) internal view returns (address result) {
|
|
/// @solidity memory-safe-assembly
|
|
assembly {
|
|
// Copy the free memory pointer so that we can restore it later.
|
|
let m := mload(0x40)
|
|
// Directly copy `r` and `s` from the calldata.
|
|
calldatacopy(0x40, signature.offset, 0x40)
|
|
// Store the `hash` in the scratch space.
|
|
mstore(0x00, hash)
|
|
// Compute `v` and store it in the scratch space.
|
|
mstore(
|
|
0x20,
|
|
byte(
|
|
0,
|
|
calldataload(add(signature.offset, 0x40))
|
|
)
|
|
)
|
|
pop(
|
|
staticcall(
|
|
gas(), // Amount of gas left for the transaction.
|
|
and(
|
|
// If the signature is exactly 65 bytes in length.
|
|
eq(signature.length, 65),
|
|
// If `s` in lower half order, such that the signature is not malleable.
|
|
lt(
|
|
mload(0x60),
|
|
add(_MALLEABILITY_THRESHOLD, 1)
|
|
)
|
|
), // Address of `ecrecover`.
|
|
0x00, // Start of input.
|
|
0x80, // Size of input.
|
|
0x00, // Start of output.
|
|
0x20 // Size of output.
|
|
)
|
|
)
|
|
result := mload(0x00)
|
|
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
|
|
if iszero(returndatasize()) {
|
|
// Store the function selector of `InvalidSignature()`.
|
|
mstore(0x00, 0x8baa579f)
|
|
// Revert with (offset, size).
|
|
revert(0x1c, 0x04)
|
|
}
|
|
// Restore the zero slot.
|
|
mstore(0x60, 0)
|
|
// Restore the free memory pointer.
|
|
mstore(0x40, m)
|
|
}
|
|
}
|
|
|
|
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
|
|
/// This produces a hash corresponding to the one signed with the
|
|
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
|
|
/// JSON-RPC method as part of EIP-191.
|
|
function toEthSignedMessageHash(
|
|
bytes32 hash
|
|
) internal pure returns (bytes32 result) {
|
|
/// @solidity memory-safe-assembly
|
|
assembly {
|
|
// Store into scratch space for keccak256.
|
|
mstore(0x20, hash)
|
|
mstore(
|
|
0x00,
|
|
"\x00\x00\x00\x00\x19Ethereum Signed Message:\n32"
|
|
)
|
|
// 0x40 - 0x04 = 0x3c
|
|
result := keccak256(0x04, 0x3c)
|
|
}
|
|
}
|
|
}
|