Last active
March 8, 2023 19:22
-
-
Save Agusx1211/bbf64f8231a96d93e9c9cf2880cc0b08 to your computer and use it in GitHub Desktop.
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
pragma solidity ^0.8.18; | |
// As per ERC-1271 | |
interface IERC1271Wallet { | |
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue); | |
} | |
error ERC1271Error(string reason); | |
contract UniversalSigValidator { | |
bytes32 private constant ERC6492_DETECTION_SUFFIX = 0x6492649264926492649264926492649264926492649264926492649264926492; | |
bytes4 private constant ERC1271_SUCCESS = 0x1626ba7e; | |
function isValidSigWithSideEffects2( | |
address _signer, | |
bytes32 _hash, | |
bytes calldata _signature | |
) public returns (bool, bool) { | |
uint contractCodeLen = address(_signer).code.length; | |
bool hadSideEffects = false; | |
// The order here is striclty defined in https://eips.ethereum.org/EIPS/eip-6492 | |
// - ERC-6492 suffix check and verification first, while being permissive in case the contract is already deployed so as to not invalidate old sigs | |
// - ERC-1271 verification if there's contract code | |
// - finally, ecrecover | |
bool isCounterfactual = bytes32(_signature[_signature.length-32:_signature.length]) == ERC6492_DETECTION_SUFFIX; | |
if (isCounterfactual) { | |
// resize the sig to remove the magic suffix | |
_signature = _signature[0:_signature.length-32]; | |
address create2Factory; | |
bytes memory factoryCalldata; | |
bytes memory originalSig; | |
(create2Factory, factoryCalldata, originalSig) = abi.decode(_signature, (address, bytes, bytes)); | |
if (contractCodeLen == 0) { | |
(bool success, ) = create2Factory.call(factoryCalldata); | |
require(success, 'SignatureValidator: deployment'); | |
hadSideEffects = true; | |
} | |
} | |
if (isCounterfactual || contractCodeLen > 0) { | |
bytes4 magicValue = IERC1271Wallet(_signer).isValidSignature(_hash, _signature); | |
return (magicValue == ERC1271_SUCCESS, hadSideEffects); | |
} | |
// ecrecover verification | |
require(_signature.length == 65, 'SignatureValidator#recoverSigner: invalid signature length'); | |
bytes32 r = bytes32(_signature[0:32]); | |
bytes32 s = bytes32(_signature[32:64]); | |
uint8 v = uint8(_signature[64]); | |
if (v != 27 && v != 28) { | |
revert('SignatureValidator#recoverSigner: invalid signature v value'); | |
} | |
return (ecrecover(_hash, v, r, s) == _signer, hadSideEffects); | |
} | |
function isValidSigWithSideEffects( | |
address _signer, | |
bytes32 _hash, | |
bytes calldata _signature | |
) external returns (bool) { | |
(bool isValid,) = isValidSigWithSideEffects2(_signer, _hash, _signature); | |
return isValid; | |
} | |
function isValidSigNoSideEffects( | |
address _signer, | |
bytes32 _hash, | |
bytes calldata _signature | |
) external returns (bool) { | |
(bool success, bytes memory data) = address(this).call( | |
abi.encodeWithSelector( | |
this.isValidSigWithSideEffects2.selector, | |
_signer, | |
_hash, | |
_signature | |
) | |
); | |
if (!success) { | |
// if the call failed, assume invalid | |
return false; | |
} | |
(bool isValid, bool hadSideEffects) = abi.decode(data, (bool, bool)); | |
if (hadSideEffects) { | |
// if the call had side effects we need to return the | |
// result using a `revert` (to undo the state changes) | |
assembly { | |
mstore(0, isValid) | |
revert(0, 32) | |
} | |
} | |
return isValid; | |
} | |
// In order to have a no side effects function, we need to call something that always reverts and then parse it's result | |
function isValidSig( | |
address _signer, | |
bytes32 _hash, | |
bytes calldata _signature | |
) external returns (bool) { | |
(,bytes memory result) = address(this).call( | |
abi.encodeWithSelector( | |
this.isValidSigNoSideEffects.selector, | |
_signer, | |
_hash, | |
_signature | |
) | |
); | |
// the self-call always returns a bool | |
return abi.decode(result, (bool)); | |
} | |
} | |
// this is a helper so we can perform validation in a single eth_call without pre-deploying a singleton | |
contract ValidateSig { | |
constructor (address _signer, bytes32 _hash, bytes memory _signature) { | |
UniversalSigValidator validator = new UniversalSigValidator(); | |
bool isValidSig = validator.isValidSigWithSideEffects(_signer, _hash, _signature); | |
assembly { | |
mstore(0, isValidSig) | |
return(31, 1) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment