Last active
September 7, 2022 14:54
-
-
Save luke7211/2d31cc47d9f604c77d75362ce86f4b05 to your computer and use it in GitHub Desktop.
Permit with AA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
library ECDSA { | |
enum RecoverError { | |
NoError, | |
InvalidSignature, | |
InvalidSignatureLength, | |
InvalidSignatureS, | |
InvalidSignatureV | |
} | |
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { | |
// Check the signature length | |
// - case 65: r,s,v signature (standard) | |
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ | |
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. | |
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 if (signature.length == 64) { | |
bytes32 r; | |
bytes32 vs; | |
// ecrecover takes the signature parameters, and the only way to get them | |
// currently is to use assembly. | |
assembly { | |
r := mload(add(signature, 0x20)) | |
vs := mload(add(signature, 0x40)) | |
} | |
return tryRecover(hash, r, vs); | |
} else { | |
return (address(0), RecoverError.InvalidSignatureLength); | |
} | |
} | |
/** | |
* @dev Overload of {ECDSA-tryRecover} that receives the `v`, | |
* `r` and `s` signature fields separately. | |
* | |
* _Available since v4.3._ | |
*/ | |
function tryRecover( | |
bytes32 hash, | |
uint8 v, | |
bytes32 r, | |
bytes32 s | |
) internal pure returns (address, RecoverError) { | |
// 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); | |
} | |
if (v != 27 && v != 28) { | |
return (address(0), RecoverError.InvalidSignatureV); | |
} | |
// 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); | |
} | |
return (signer, RecoverError.NoError); | |
} | |
/** | |
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. | |
* | |
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] | |
* | |
* _Available since v4.3._ | |
*/ | |
function tryRecover( | |
bytes32 hash, | |
bytes32 r, | |
bytes32 vs | |
) internal pure returns (address, RecoverError) { | |
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); | |
uint8 v = uint8((uint256(vs) >> 255) + 27); | |
return tryRecover(hash, v, r, s); | |
} | |
} | |
interface IERC1271 { | |
/** | |
* @dev Should return whether the signature provided is valid for the provided data | |
* @param hash Hash of the data to be signed | |
* @param signature Signature byte array associated with _data | |
*/ | |
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); | |
} | |
function isValidSignatureNow( | |
address signer, | |
bytes32 hash, | |
bytes memory signature | |
) internal view returns (bool) { | |
(address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature); | |
if (error == ECDSA.RecoverError.NoError && recovered == signer) { | |
return true; | |
} | |
(bool success, bytes memory result) = signer.staticcall( | |
abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature) | |
); | |
return (success && | |
result.length == 32 && | |
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector)); | |
} | |
function permit( | |
address owner, | |
address spender, | |
uint256 value, | |
uint256 deadline, | |
bytes signature, | |
) external { | |
require(block.timestamp <= deadline, "Dai/permit-expired"); | |
require (owner != address(0), "Dai/invalid-permit"); | |
uint256 chainId; | |
assembly { | |
chainId := chainid() | |
} | |
bytes32 digest = keccak256( | |
abi.encodePacked( | |
"\x19\x01", | |
chainId == deploymentChainId ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(chainId), | |
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) | |
) | |
); | |
require(isValidSignatureNow(owner, digest, signature)); | |
allowance[owner][spender] = value; | |
emit Approval(owner, spender, value); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment