Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Minimal ERC6492 non-reverting verifier #1221

Merged
merged 4 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions src/utils/SignatureCheckerLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ library SignatureCheckerLib {
/// If the signature is postfixed with the ERC6492 magic number, it will attempt to
/// deploy / prepare the `signer` smart account before doing a regular ERC1271 check.
/// Note: This function is NOT reentrancy safe.
/// The verifier must be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/011d6becff6e0a73e42fe100f8d7ef04
/// With a dedicated verifier, this function is safe to use in contracts
/// that have been granted special permissions.
function isValidERC6492SignatureNowAllowSideEffects(
address signer,
bytes32 hash,
Expand All @@ -329,21 +334,26 @@ library SignatureCheckerLib {
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
break
}
let o := add(signature, 0x20) // Signature bytes.
let d := add(o, mload(add(o, 0x20))) // Factory calldata.
if noCode {
if iszero(call(gas(), mload(o), 0, add(d, 0x20), mload(d), codesize(), 0x00)) {
break
}
}
let s := add(o, mload(add(o, 0x40))) // Inner signature.
isValid := callIsValidSignature(signer, hash, s)
if iszero(isValid) {
if call(gas(), mload(o), 0, add(d, 0x20), mload(d), codesize(), 0x00) {
noCode := iszero(extcodesize(signer))
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, s) }
}
if iszero(noCode) {
let o := add(signature, 0x20) // Signature bytes.
isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
if isValid { break }
}
let m := mload(0x40)
mstore(m, signer)
mstore(add(m, 0x20), hash)
pop(
call(
gas(), // Remaining gas.
0x0000bc370E4DC924F427d84e2f4B9Ec81626ba7E, // Non-reverting verifier.
0, // Send zero ETH.
m, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome trick.

0x00 // Length of returndata to write.
)
)
isValid := returndatasize()
break
}
// Do `ecrecover` fallback if `noCode && !isValid`.
Expand Down
15 changes: 15 additions & 0 deletions test/SignatureCheckerLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ contract SignatureCheckerLibTest is SoladyTest {
}

function testERC6492AllowSideEffectsPostDeploy() public {
_etchERC6492Verifier();
_ERC6492TestTemps memory t = _erc6492TestTemps();
(bool success,) = t.factory.call(t.factoryCalldata);
require(success);
Expand All @@ -491,6 +492,7 @@ contract SignatureCheckerLibTest is SoladyTest {
}

function testERC6492AllowSideEffectsPreDeploy() public {
_etchERC6492Verifier();
_ERC6492TestTemps memory t = _erc6492TestTemps();
t.result = SignatureCheckerLib.isValidERC6492SignatureNowAllowSideEffects(
t.smartAccount, t.digest, t.innerSignature
Expand Down Expand Up @@ -522,6 +524,19 @@ contract SignatureCheckerLibTest is SoladyTest {
assertEq(MockERC1271Wallet(t.smartAccount).signer(), t.eoa);
}

function _etchERC6492Verifier() internal returns (address verifier) {
_ERC6492TestTemps memory t;
t.initcode =
hex"6040600b3d3960403df3fe36383d373d3d6020515160208051013d3d515af160203851516084018038385101606037303452813582523838523490601c34355afa34513060e01b141634f3";
t.factory = _NICKS_FACTORY;
t.salt = 0x0000000000000000000000000000000000000000ebfa269e1c28e801a0dc87e2;
verifier = LibClone.predictDeterministicAddress(keccak256(t.initcode), t.salt, t.factory);
assertEq(_nicksCreate2(0, t.salt, t.initcode), verifier);
assertGt(verifier.code.length, 0);
emit LogBytes32(keccak256(t.initcode));
emit LogBytes(verifier.code);
}

function _etchERC6492RevertingVerifier() internal returns (address revertingVerifier) {
_ERC6492TestTemps memory t;
t.initcode =
Expand Down
Loading