From 5c0658bd568eb98f5c4be7a9ff29ca1545eee50c Mon Sep 17 00:00:00 2001 From: Bashmunta Date: Mon, 17 Nov 2025 09:20:25 +0200 Subject: [PATCH 1/4] Optimize ERC-1271 path in isValidSignatureNowCalldata to avoid extra allocation --- .../utils/cryptography/SignatureChecker.sol | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/contracts/utils/cryptography/SignatureChecker.sol b/contracts/utils/cryptography/SignatureChecker.sol index de619568990..121229fe1ef 100644 --- a/contracts/utils/cryptography/SignatureChecker.sol +++ b/contracts/utils/cryptography/SignatureChecker.sol @@ -50,7 +50,7 @@ library SignatureChecker { (address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecoverCalldata(hash, signature); return err == ECDSA.RecoverError.NoError && recovered == signer; } else { - return isValidERC1271SignatureNow(signer, hash, signature); + return isValidERC1271SignatureNowCalldata(signer, hash, signature); } } @@ -73,8 +73,8 @@ library SignatureChecker { // Encoded calldata is : // [ 0x00 - 0x03 ] // [ 0x04 - 0x23 ] - // [ 0x24 - 0x44 ] (0x40) - // [ 0x44 - 0x64 ] + // [ 0x24 - 0x43 ] (0x40) + // [ 0x44 - 0x63 ] // [ 0x64 - ... ] let ptr := mload(0x40) mstore(ptr, selector) @@ -87,6 +87,33 @@ library SignatureChecker { } } + function isValidERC1271SignatureNowCalldata( + address signer, + bytes32 hash, + bytes calldata signature + ) internal view returns (bool result) { + bytes4 selector = IERC1271.isValidSignature.selector; + + assembly ("memory-safe") { + // Encoded calldata is : + // [ 0x00 - 0x03 ] + // [ 0x04 - 0x23 ] + // [ 0x24 - 0x43 ] (0x40) + // [ 0x44 - 0x63 ] + // [ 0x64 - ... ] + let ptr := mload(0x40) + mstore(ptr, selector) + mstore(add(ptr, 0x04), hash) + mstore(add(ptr, 0x24), 0x40) + let length := signature.length + mstore(add(ptr, 0x44), length) + calldatacopy(add(ptr, 0x64), signature.offset, length) + + let success := staticcall(gas(), signer, ptr, add(length, 0x64), 0x00, 0x20) + result := and(success, and(gt(returndatasize(), 0x1f), eq(mload(0x00), selector))) + } + } + /** * @dev Verifies a signature for a given ERC-7913 signer and hash. * From 0ad378ed970c16b24e9f419a5921ca2ed8e0332b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 25 Nov 2025 17:08:36 +0100 Subject: [PATCH 2/4] Update SignatureChecker.sol --- contracts/utils/cryptography/SignatureChecker.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/utils/cryptography/SignatureChecker.sol b/contracts/utils/cryptography/SignatureChecker.sol index 121229fe1ef..ab47f96316c 100644 --- a/contracts/utils/cryptography/SignatureChecker.sol +++ b/contracts/utils/cryptography/SignatureChecker.sol @@ -93,6 +93,7 @@ library SignatureChecker { bytes calldata signature ) internal view returns (bool result) { bytes4 selector = IERC1271.isValidSignature.selector; + uint256 length = signature.length; assembly ("memory-safe") { // Encoded calldata is : @@ -105,7 +106,6 @@ library SignatureChecker { mstore(ptr, selector) mstore(add(ptr, 0x04), hash) mstore(add(ptr, 0x24), 0x40) - let length := signature.length mstore(add(ptr, 0x44), length) calldatacopy(add(ptr, 0x64), signature.offset, length) From 6e489e70baa00c1a6d8bb07d40baebd9d45e9ca5 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 26 Nov 2025 16:03:59 +0100 Subject: [PATCH 3/4] testing --- test/utils/cryptography/SignatureChecker.test.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/utils/cryptography/SignatureChecker.test.js b/test/utils/cryptography/SignatureChecker.test.js index 82561d53877..99b4f694adc 100644 --- a/test/utils/cryptography/SignatureChecker.test.js +++ b/test/utils/cryptography/SignatureChecker.test.js @@ -58,7 +58,12 @@ describe('SignatureChecker (ERC1271)', function () { }); describe('ERC1271 wallet', function () { - for (const fn of ['isValidERC1271SignatureNow', 'isValidSignatureNow', 'isValidSignatureNowCalldata']) { + for (const fn of [ + 'isValidERC1271SignatureNow', + 'isValidERC1271SignatureNowCalldata', + 'isValidSignatureNow', + 'isValidSignatureNowCalldata', + ]) { describe(fn, function () { it('with matching signer and signature', async function () { await expect( From 0f5d11bfcf0ab5013a6a53898152d16f26f31351 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 26 Nov 2025 16:05:16 +0100 Subject: [PATCH 4/4] Add changeset entry --- .changeset/fuzzy-lizards-do.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fuzzy-lizards-do.md diff --git a/.changeset/fuzzy-lizards-do.md b/.changeset/fuzzy-lizards-do.md new file mode 100644 index 00000000000..02545606de7 --- /dev/null +++ b/.changeset/fuzzy-lizards-do.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': minor +--- + +`SignatureChecker`: Add `isValidERC1271SignatureNowCalldata`, a variant of `isValidERC1271SignatureNow` that takes the signature from calldata.