// 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 defined by `v`, `r`, `s`. function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) 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) mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) pop( staticcall( gas(), // Amount of gas left for the transaction. // If `s` in lower half order, such that the signature is not malleable. lt(s, 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) } } }