Skip to content

Commit

Permalink
✨ isValidERC6492SignatureNow (#941)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed Jun 3, 2024
1 parent b15cf43 commit d315b01
Show file tree
Hide file tree
Showing 4 changed files with 372 additions and 29 deletions.
62 changes: 34 additions & 28 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ ERC1155Test:testSafeTransferFromToZeroReverts(uint256) (runs: 278, μ: 71146, ~:
ERC1155Test:test__codesize() (gas: 42341)
ERC1271Test:testBasefeeBytecodeContract() (gas: 45430)
ERC1271Test:testIsValidSignature() (gas: 2464705)
ERC1271Test:testIsValidSignature(uint256) (runs: 278, μ: 265900, ~: 215838)
ERC1271Test:testIsValidSignature(uint256) (runs: 278, μ: 251119, ~: 213071)
ERC1271Test:test__codesize() (gas: 30438)
ERC1967FactoryTest:testChangeAdmin() (gas: 266356)
ERC1967FactoryTest:testChangeAdminUnauthorized() (gas: 257653)
Expand All @@ -194,7 +194,7 @@ ERC1967FactoryTest:testUpgradeAndCallWithRevert() (gas: 265824)
ERC1967FactoryTest:testUpgradeUnauthorized() (gas: 270340)
ERC1967FactoryTest:testUpgradeWithCorruptedProxy() (gas: 263294)
ERC1967FactoryTest:test__codesize() (gas: 32011)
ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 128000, reverts: 78059)
ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 128000, reverts: 78163)
ERC20Invariants:test__codesize() (gas: 7532)
ERC20Test:testApprove() (gas: 35730)
ERC20Test:testApprove(address,uint256) (runs: 278, μ: 30751, ~: 31181)
Expand Down Expand Up @@ -229,32 +229,32 @@ ERC2981Test:testRoyaltyOverflowCheckDifferential(uint256,uint256) (runs: 278, μ
ERC2981Test:testSetAndGetRoyaltyInfo(uint256) (runs: 278, μ: 109507, ~: 105299)
ERC2981Test:test__codesize() (gas: 7113)
ERC4337FactoryTest:testCreateAccountRepeatedDeployment() (gas: 149757)
ERC4337FactoryTest:testCreateAccountRepeatedDeployment(uint256) (runs: 278, μ: 171721, ~: 171739)
ERC4337FactoryTest:testDeployDeterministic(uint256) (runs: 278, μ: 130904, ~: 139893)
ERC4337FactoryTest:testCreateAccountRepeatedDeployment(uint256) (runs: 278, μ: 171722, ~: 171739)
ERC4337FactoryTest:testDeployDeterministic(uint256) (runs: 278, μ: 129475, ~: 139893)
ERC4337FactoryTest:test__codesize() (gas: 12786)
ERC4337Test:testCdFallback() (gas: 438129)
ERC4337Test:testCdFallback2() (gas: 1135369)
ERC4337Test:testDelegateExecute() (gas: 366495)
ERC4337Test:testDelegateExecute(uint256) (runs: 278, μ: 351662, ~: 345338)
ERC4337Test:testDelegateExecute(uint256) (runs: 278, μ: 353204, ~: 345338)
ERC4337Test:testDelegateExecuteRevertsIfOwnerSlotValueChanged() (gas: 320155)
ERC4337Test:testDepositFunctions() (gas: 497250)
ERC4337Test:testDirectStorage() (gas: 70435)
ERC4337Test:testDisableInitializerForImplementation() (gas: 1320997)
ERC4337Test:testETHReceived() (gas: 16584)
ERC4337Test:testExecute() (gas: 383602)
ERC4337Test:testExecuteBatch() (gas: 694209)
ERC4337Test:testExecuteBatch(uint256) (runs: 278, μ: 537943, ~: 669873)
ERC4337Test:testExecuteBatch(uint256) (runs: 278, μ: 532098, ~: 669723)
ERC4337Test:testInitializer() (gas: 285472)
ERC4337Test:testIsValidSignature() (gas: 120064)
ERC4337Test:testIsValidSignaturePersonalSign() (gas: 103314)
ERC4337Test:testIsValidSignatureViaRPC() (gas: 96881549)
ERC4337Test:testIsValidSignatureWrapped() (gas: 416253)
ERC4337Test:testIsValidSignatureWrapped() (gas: 446564)
ERC4337Test:testOnERC1155BatchReceived() (gas: 1569677)
ERC4337Test:testOnERC1155Received() (gas: 1567008)
ERC4337Test:testOnERC721Received() (gas: 1601995)
ERC4337Test:testOwnerRecovery() (gas: 479866)
ERC4337Test:testValidateUserOp() (gas: 484978)
ERC4337Test:test__codesize() (gas: 57203)
ERC4337Test:test__codesize() (gas: 57354)
ERC4626Test:testDepositWithNoApprovalReverts() (gas: 16569)
ERC4626Test:testDepositWithNotEnoughApprovalReverts() (gas: 90179)
ERC4626Test:testDifferentialFullMulDiv(uint256,uint256,uint256) (runs: 278, μ: 3316, ~: 3107)
Expand All @@ -276,21 +276,21 @@ ERC4626Test:testWithdrawZero() (gas: 51874)
ERC4626Test:test__codesize() (gas: 37175)
ERC6551Test:testBaseFeeMini() (gas: 39514)
ERC6551Test:testCdFallback() (gas: 895594)
ERC6551Test:testDeployERC6551(uint256) (runs: 278, μ: 170351, ~: 168891)
ERC6551Test:testDeployERC6551(uint256) (runs: 278, μ: 170101, ~: 168890)
ERC6551Test:testDeployERC6551Proxy() (gas: 80395)
ERC6551Test:testExecute() (gas: 507518)
ERC6551Test:testExecuteBatch() (gas: 817605)
ERC6551Test:testExecuteBatch(uint256) (runs: 278, μ: 639020, ~: 762714)
ERC6551Test:testExecuteBatch(uint256) (runs: 278, μ: 641457, ~: 762990)
ERC6551Test:testInitializeERC6551ProxyImplementation() (gas: 189914)
ERC6551Test:testIsValidSigner(address) (runs: 278, μ: 167471, ~: 167467)
ERC6551Test:testIsValidSigner(address) (runs: 278, μ: 167465, ~: 167466)
ERC6551Test:testOnERC1155BatchReceived() (gas: 1702519)
ERC6551Test:testOnERC1155Received() (gas: 1699882)
ERC6551Test:testOnERC721Received() (gas: 1738866)
ERC6551Test:testOnERC721ReceivedCycles() (gas: 2914176)
ERC6551Test:testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) (runs: 278, μ: 450753, ~: 455666)
ERC6551Test:testOwnerWorksWithChainIdChange(uint256,uint256) (runs: 278, μ: 1368680, ~: 1368690)
ERC6551Test:testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) (runs: 278, μ: 451105, ~: 455649)
ERC6551Test:testOwnerWorksWithChainIdChange(uint256,uint256) (runs: 278, μ: 1368677, ~: 1368690)
ERC6551Test:testSupportsInterface() (gas: 169450)
ERC6551Test:testUpdateState(uint256) (runs: 278, μ: 235211, ~: 235131)
ERC6551Test:testUpdateState(uint256) (runs: 278, μ: 235197, ~: 235130)
ERC6551Test:testUpgrade() (gas: 248450)
ERC6551Test:test__codesize() (gas: 52354)
ERC6909Test:testApprove() (gas: 36872)
Expand Down Expand Up @@ -1156,21 +1156,27 @@ SafeTransferLibTest:testTryTransferETH() (gas: 148701)
SafeTransferLibTest:testTryTransferETHWithNoGrief() (gas: 537025)
SafeTransferLibTest:testTryTransferETHWithNoStorageWrites() (gas: 192444)
SafeTransferLibTest:test__codesize() (gas: 67852)
SignatureCheckerLibTest:testEmptyCalldataHelpers() (gas: 3965)
SignatureCheckerLibTest:testSignatureChecker(bytes32) (runs: 278, μ: 58138, ~: 57960)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSignature() (gas: 21259)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSigner() (gas: 30759)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithMatchingSignerAndSignature() (gas: 17634)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithWrongSignedMessageHash() (gas: 21216)
SignatureCheckerLibTest:testSignatureCheckerOnMaliciousWallet() (gas: 31683)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSignature() (gas: 77130)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSigner() (gas: 25472)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithMatchingSignerAndSignature() (gas: 64074)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithWrongSignedMessageHash() (gas: 64131)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithZeroAddressSigner() (gas: 12237)
SignatureCheckerLibTest:testERC6492AllowSideEffectsPostDeploy() (gas: 444891)
SignatureCheckerLibTest:testERC6492AllowSideEffectsPreDeploy() (gas: 450591)
SignatureCheckerLibTest:testERC6492PostDeploy() (gas: 496287)
SignatureCheckerLibTest:testERC6492PreDeploy() (gas: 1227387)
SignatureCheckerLibTest:testERC6492WithoutRevertingVerifier() (gas: 61898)
SignatureCheckerLibTest:testEmptyCalldataHelpers() (gas: 3987)
SignatureCheckerLibTest:testEtchERC6492RevertingVerifier() (gas: 56459)
SignatureCheckerLibTest:testSignatureChecker(bytes32) (runs: 278, μ: 58257, ~: 58495)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSignature() (gas: 21288)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSigner() (gas: 30788)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithMatchingSignerAndSignature() (gas: 17685)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithWrongSignedMessageHash() (gas: 21245)
SignatureCheckerLibTest:testSignatureCheckerOnMaliciousWallet() (gas: 31806)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSignature() (gas: 77490)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSigner() (gas: 25728)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithMatchingSignerAndSignature() (gas: 64494)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithWrongSignedMessageHash() (gas: 64573)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithZeroAddressSigner() (gas: 12340)
SignatureCheckerLibTest:testToEthSignedMessageHashDifferential(bytes) (runs: 278, μ: 1285, ~: 1284)
SignatureCheckerLibTest:testToEthSignedMessageHashDifferential(bytes32) (runs: 278, μ: 481, ~: 481)
SignatureCheckerLibTest:test__codesize() (gas: 9970)
SignatureCheckerLibTest:test__codesize() (gas: 15523)
SoladyTest:test__codesize() (gas: 840)
TestPlus:test__codesize() (gas: 406)
UUPSUpgradeableTest:testNotDelegatedGuard() (gas: 15875)
Expand All @@ -1191,7 +1197,7 @@ UpgradeableBeaconTest:testUpgradeableSolidityBeaconOnlyOwnerFunctions() (gas: 26
UpgradeableBeaconTest:testUpgradeableYulBeaconOnlyFnSelectorNotRecognised() (gas: 172796)
UpgradeableBeaconTest:testUpgradeableYulBeaconOnlyOwnerFunctions() (gas: 198398)
UpgradeableBeaconTest:test__codesize() (gas: 8808)
WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 128000, reverts: 59538)
WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 128000, reverts: 59103)
WETHInvariants:test__codesize() (gas: 5264)
WETHTest:testDeposit() (gas: 68090)
WETHTest:testDeposit(uint256) (runs: 278, μ: 67950, ~: 68384)
Expand Down
121 changes: 121 additions & 0 deletions src/utils/SignatureCheckerLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ library SignatureCheckerLib {
/* ERC1271 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

// Note: These ERC1271 operations do NOT have an ECDSA fallback.
// These functions are intended to be used with the regular `isValidSignatureNow` functions
// or other signature verification functions (e.g. P256).

/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
Expand Down Expand Up @@ -486,6 +490,123 @@ library SignatureCheckerLib {
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC6492 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

// Note: These ERC6492 operations do NOT have an ECDSA fallback.
// These functions are intended to be used with the regular `isValidSignatureNow` functions
// or other signature verification functions (e.g. P256).
// The calldata variants are excluded for brevity.

/// @dev Returns whether `signature` is valid for `hash`.
/// 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.
function isValidERC6492SignatureNowAllowSideEffects(
address signer,
bytes32 hash,
bytes memory signature
) internal returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
pop(staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_))
_isValid :=
and(
eq(mload(d_), f_),
staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
)
}
for { let n := mload(signature) } 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
isValid := callIsValidSignature(signer, hash, signature)
break
}
let o := add(signature, 0x20) // Signature bytes.
let d := add(o, mload(add(o, 0x20))) // Factory calldata.
if iszero(extcodesize(signer)) {
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) {
isValid := callIsValidSignature(signer, hash, s)
}
}
break
}
}
}

/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt
/// to use a reverting verifier to deploy / prepare the `signer` smart account
/// and do a `isValidSignature` check via the reverting verifier.
/// Note: This function is reentrancy safe.
/// The reverting verifier must be be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/846a474c855eee9e441506676800a9ad
function isValidERC6492SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
pop(staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_))
_isValid :=
and(
eq(mload(d_), f_),
staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
)
}
for { let n := mload(signature) } 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
isValid := callIsValidSignature(signer, hash, signature)
break
}
if extcodesize(signer) {
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)
let willBeZeroIfRevertingVerifierExists :=
call(
gas(), // Remaining gas.
0x00007bd799e4A591FeA53f8A8a3E9f931626Ba7e, // 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.
0x00 // Length of returndata to write.
)
isValid := gt(returndatasize(), willBeZeroIfRevertingVerifierExists)
break
}
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down

0 comments on commit d315b01

Please sign in to comment.