Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
f5544fd
Add Account framework
ernestognw May 2, 2025
40dfd20
Add missing mocks
ernestognw May 2, 2025
0bcc521
Adding missing hardhat config
ernestognw May 2, 2025
7e75026
up
ernestognw May 2, 2025
5fb074c
Remove unnecessary files for mocks
ernestognw May 2, 2025
e5df541
Remove more unnecessary mock files
ernestognw May 2, 2025
5ad9788
replace hardcoded links
ernestognw May 2, 2025
7090f67
lockfile
ernestognw May 2, 2025
c4af1d7
update ethers
ernestognw May 2, 2025
aa8f29b
add missing interface
ernestognw May 2, 2025
415c00d
Add changesets
ernestognw May 2, 2025
f60aa3a
up
ernestognw May 2, 2025
087a844
up
ernestognw May 2, 2025
79629b7
up
ernestognw May 2, 2025
f47cab7
up
ernestognw May 2, 2025
8f58197
up
ernestognw May 2, 2025
ecede7f
up
ernestognw May 2, 2025
68bd96a
up
ernestognw May 2, 2025
d5cb119
chore: empty commit
ernestognw May 2, 2025
6a0ae8a
change read permissions
ernestognw May 2, 2025
a49a157
Update lucky-donuts-scream.md
ernestognw May 2, 2025
a634278
Update clean-ways-push.md
ernestognw May 2, 2025
8b6501a
Update tame-bears-mix.md
ernestognw May 2, 2025
39a1026
reset package-lock.json
ernestognw May 2, 2025
71a6b25
up
ernestognw May 2, 2025
35d4a12
up
ernestognw May 2, 2025
a95705b
reset dependencies
ernestognw May 2, 2025
90509bd
reset dependencies
ernestognw May 2, 2025
10f40d7
reset dependencies
ernestognw May 2, 2025
36fb044
lint
ernestognw May 2, 2025
7e10f80
Attempt to fix tests
ernestognw May 2, 2025
c6ed868
up
ernestognw May 2, 2025
b87c8e2
Merge branch 'master' into feature/account-abstraction
ernestognw May 2, 2025
f6d07c2
adjust action.yml
ernestognw May 2, 2025
cfa2392
up
ernestognw May 2, 2025
9a8e63f
Merge branch 'master' into feature/account-abstraction
ernestognw May 2, 2025
3a90091
lint
ernestognw May 2, 2025
bdec803
lint
ernestognw May 2, 2025
73c12c7
Merge branch 'master' into feature/account-abstraction
ernestognw May 2, 2025
1c97739
Test ethers 6.13.6-beta.1
ernestognw May 2, 2025
6b1bbd8
up
ernestognw May 2, 2025
7764515
up
ernestognw May 2, 2025
19fe4c5
up
ernestognw May 2, 2025
11c42c3
checks
ernestognw May 2, 2025
be68753
up
ernestognw May 2, 2025
f0a1155
build in slither
ernestognw May 2, 2025
c42a7fd
Update build command
ernestognw May 2, 2025
6e576ca
compile hardhat too
ernestognw May 2, 2025
593e879
revert slither changes
ernestognw May 2, 2025
c3f39a1
Remove package-lock.json to skip installing dependencies
ernestognw May 2, 2025
db76c3b
up
ernestognw May 2, 2025
8eebff0
Add @custom:stateless tag
ernestognw May 3, 2025
65fa7de
update upgradeable.patch
ernestognw May 3, 2025
abac3bd
fix conflicts
ernestognw May 3, 2025
7d120b9
rollback <package-version>
ernestognw May 3, 2025
02eccc1
update upgradeable.patch
ernestognw May 3, 2025
c39d5f5
Tweak workflows
ernestognw May 3, 2025
54f632a
Use Solidity 0.8.27 as default and set default EVM to prague
ernestognw May 3, 2025
58c794e
Adjust ERC2771Forwarder gas to avoid GasFloorMoreThanGasLimit
ernestognw May 3, 2025
6a60523
Remove console.log
ernestognw May 3, 2025
80edba8
Add EnumerableSetExtended and EnumerableMapExtended
ernestognw May 2, 2025
29c48d9
Fix lint and enable formatting after generation
ernestognw May 3, 2025
fae0a67
Add ERC7913 signers and utilities
ernestognw May 2, 2025
e412bd9
Add EnumerableSetExtended and EnumerableMapExtended
ernestognw May 2, 2025
f7f64ee
Add changeset and fix linting
ernestognw May 3, 2025
b0b70eb
Merge branch 'master' into feature/enumerable-extended
ernestognw Jun 2, 2025
b695659
Remove TODOs
ernestognw Jun 2, 2025
5c4fb88
Reset package-lock
ernestognw Jun 2, 2025
8f32638
Revert run.js
ernestognw Jun 2, 2025
b12ca61
Merge Enumerable{Set,Map}Extended into Enumerable{Set,Map}
Amxx Jun 2, 2025
45fa35f
Update scripts/generate/templates/Enumerable.opts.js
Amxx Jun 2, 2025
bec8059
clarification
Amxx Jun 2, 2025
0d7f9d0
speedup generation with selective linter
Amxx Jun 2, 2025
2376a45
update documentation
Amxx Jun 2, 2025
1e35eab
Merge master
ernestognw Jun 2, 2025
c275c03
Remove unnecesary code
ernestognw Jun 2, 2025
5975d79
Remove Bytes32x2
ernestognw Jun 2, 2025
9356599
Update .changeset/pink-dolls-shop.md
ernestognw Jun 2, 2025
86c2cb8
Remove unnecessary _hashes
ernestognw Jun 2, 2025
6898230
Simplify
ernestognw Jun 2, 2025
4e2bc70
Improve changesets
ernestognw Jun 2, 2025
2a1f503
remove unecessary import
Amxx Jun 2, 2025
326c466
Use Arrays.sol
ernestognw Jun 2, 2025
52ac908
Merge branch 'feature/enumerable-extended' into feature/erc7913
ernestognw Jun 3, 2025
6725618
up
ernestognw Jun 3, 2025
0cce4d5
up
ernestognw Jun 3, 2025
5c45ff0
Merge branch 'master' into feature/erc7913
Amxx Jun 3, 2025
80d8bd5
Merge branch 'master' into feature/erc7913
ernestognw Jun 3, 2025
30f3bfa
cleanup
ernestognw Jun 3, 2025
eab64f8
Remove EnumerableSetExtended usage
ernestognw Jun 3, 2025
e3dcc2b
Add tests
ernestognw Jun 3, 2025
3ac675d
Add changesets
ernestognw Jun 3, 2025
b20b017
Merge branch 'master' into feature/erc7913
ernestognw Jun 4, 2025
804ceea
Organize
ernestognw Jun 4, 2025
d0f5961
Review
ernestognw Jun 4, 2025
98108d9
Increase SignatureChecker's pragma
ernestognw Jun 4, 2025
e1ffe05
Pragma consistency
ernestognw Jun 4, 2025
9a32135
Increase SignatureChecker minimum pragma to 0.8.24
ernestognw Jun 4, 2025
d8e132a
Update mocks
ernestognw Jun 4, 2025
f2406ca
Merge branch 'master' into feature/erc7913
Amxx Jun 4, 2025
2dd9f27
paginated getSigner()
Amxx Jun 4, 2025
77587ad
simplify mock
Amxx Jun 4, 2025
b7ddad9
minor signer helper refactor
Amxx Jun 4, 2025
ea7ffc3
simplify helper
Amxx Jun 4, 2025
2f3f054
Merge branch 'master' into feature/erc7913
ernestognw Jun 4, 2025
6619a53
Add fallback to check uniqueness if signers are not ordered (#56)
Amxx Jun 4, 2025
cefe101
Update contracts/utils/cryptography/SignatureChecker.sol
Amxx Jun 4, 2025
b1753d1
Fix tests
ernestognw Jun 4, 2025
cd2399b
Remove unnecessary mock
ernestognw Jun 4, 2025
0b1b8eb
Docs nits
ernestognw Jun 4, 2025
e592125
Remove weighted multisig
ernestognw Jun 5, 2025
4dc17a7
Use uint64 as threshold
ernestognw Jun 5, 2025
d918e59
Add totalSigners function
ernestognw Jun 5, 2025
2bee2c4
Update contracts/utils/cryptography/signers/MultiSignerERC7913.sol
Amxx Jun 5, 2025
7977301
Apply suggestions from code review
Amxx Jun 5, 2025
781644b
remove weights from MultiERC7913SigningKey
Amxx Jun 5, 2025
2855e50
test style consistency
Amxx Jun 5, 2025
395c67a
Suggestions
ernestognw Jun 5, 2025
a6bff02
up
ernestognw Jun 5, 2025
92e4fc7
Update contracts/utils/cryptography/signers/MultiSignerERC7913.sol
ernestognw Jun 5, 2025
7c9082f
Fix compilation
ernestognw Jun 5, 2025
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
5 changes: 5 additions & 0 deletions .changeset/nice-rings-wish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`ERC7913P256Verifier` and `ERC7913RSAVerifier`: Ready to use ERC-7913 verifiers that implement key verification for P256 (secp256r1) and RSA keys.
5 changes: 5 additions & 0 deletions .changeset/quiet-kiwis-feel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`SignerERC7913`: Abstract signer that verifies signatures using the ERC-7913 workflow.
5 changes: 5 additions & 0 deletions .changeset/social-walls-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`MultiSignerERC7913`: Implementation of `AbstractSigner` that supports multiple ERC-7913 signers with a threshold-based signature verification system.
5 changes: 5 additions & 0 deletions .changeset/sour-pens-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`SignatureChecker`: Add support for ERC-7913 signatures alongside existing ECDSA and ERC-1271 signature verification.
17 changes: 17 additions & 0 deletions contracts/interfaces/IERC7913.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @dev Signature verifier interface.
*/
interface IERC7913SignatureVerifier {
/**
* @dev Verifies `signature` as a valid signature of `hash` by `key`.
*
* MUST return the bytes4 magic value IERC7913SignatureVerifier.verify.selector if the signature is valid.
* SHOULD return 0xffffffff or revert if the signature is not valid.
* SHOULD return 0xffffffff or revert if the key is empty
*/
function verify(bytes calldata key, bytes32 hash, bytes calldata signature) external view returns (bytes4);
}
33 changes: 33 additions & 0 deletions contracts/mocks/account/AccountMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {SignerECDSA} from "../../utils/cryptography/signers/SignerECDSA.sol";
import {SignerP256} from "../../utils/cryptography/signers/SignerP256.sol";
import {SignerRSA} from "../../utils/cryptography/signers/SignerRSA.sol";
import {SignerERC7702} from "../../utils/cryptography/signers/SignerERC7702.sol";
import {SignerERC7913} from "../../utils/cryptography/signers/SignerERC7913.sol";
import {MultiSignerERC7913} from "../../utils/cryptography/signers/MultiSignerERC7913.sol";

abstract contract AccountMock is Account, ERC7739, ERC7821, ERC721Holder, ERC1155Holder {
/// Validates a user operation with a boolean signature.
Expand Down Expand Up @@ -136,3 +138,34 @@ abstract contract AccountERC7579HookedMock is AccountERC7579Hooked {
_installModule(MODULE_TYPE_VALIDATOR, validator, initData);
}
}

abstract contract AccountMultiSignerMock is Account, MultiSignerERC7913, ERC7739, ERC7821, ERC721Holder, ERC1155Holder {
constructor(bytes[] memory signers, uint64 threshold) {
_addSigners(signers);
_setThreshold(threshold);
}

/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}

abstract contract AccountERC7913Mock is Account, SignerERC7913, ERC7739, ERC7821, ERC721Holder, ERC1155Holder {
constructor(bytes memory _signer) {
_setSigner(_signer);
}

/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 12 additions & 0 deletions contracts/utils/cryptography/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ A collection of contracts and libraries that implement various signature validat
* {ERC7739}: An abstract contract to validate signatures following the rehashing scheme from {ERC7739Utils}.
* {SignerECDSA}, {SignerP256}, {SignerRSA}: Implementations of an {AbstractSigner} with specific signature validation algorithms.
* {SignerERC7702}: Implementation of {AbstractSigner} that validates signatures using the contract's own address as the signer, useful for delegated accounts following EIP-7702.
* {SignerERC7913}, {MultiSignerERC7913}: Implementations of {AbstractSigner} that validate signatures based on ERC-7913. Including a simple multisignature scheme.
* {ERC7913P256Verifier}, {ERC7913RSAVerifier}: Ready to use ERC-7913 signature verifiers for P256 and RSA keys.

== Utils

Expand Down Expand Up @@ -51,3 +53,13 @@ A collection of contracts and libraries that implement various signature validat
{{SignerRSA}}

{{SignerERC7702}}

{{SignerERC7913}}

{{MultiSignerERC7913}}

== Verifiers

{{ERC7913P256Verifier}}

{{ERC7913RSAVerifier}}
91 changes: 88 additions & 3 deletions contracts/utils/cryptography/SignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,29 @@ pragma solidity ^0.8.24;

import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
import {IERC7913SignatureVerifier} from "../../interfaces/IERC7913.sol";
import {Bytes} from "../../utils/Bytes.sol";

/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC-1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support:
*
* * ECDSA signatures from externally owned accounts (EOAs)
* * ERC-1271 signatures from smart contract wallets like Argent and Safe Wallet (previously Gnosis Safe)
* * ERC-7913 signatures from keys that do not have an Ethereum address of their own
*
* See https://eips.ethereum.org/EIPS/eip-1271[ERC-1271] and https://eips.ethereum.org/EIPS/eip-7913[ERC-7913].
*/
library SignatureChecker {
using Bytes for bytes;

/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer has code, the
* signature is validated against it using ERC-1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*
* NOTE: For an extended version of this function that supports ERC-7913 signatures, see {isValidERC7913SignatureNow}.
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
if (signer.code.length == 0) {
Expand Down Expand Up @@ -47,4 +57,79 @@ library SignatureChecker {
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}

/**
* @dev Verifies a signature for a given ERC-7913 signer and hash.
*
* The signer is a `bytes` object that is the concatenation of an address and optionally a key:
* `verifier || key`. A signer must be at least 20 bytes long.
*
* Verification is done as follows:
*
* * If `signer.length < 20`: verification fails
* * If `signer.length == 20`: verification is done using {isValidSignatureNow}
* * Otherwise: verification is done using {IERC7913SignatureVerifier}
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC7913SignatureNow(
bytes memory signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
if (signer.length < 20) {
return false;
} else if (signer.length == 20) {
return isValidSignatureNow(address(bytes20(signer)), hash, signature);
} else {
(bool success, bytes memory result) = address(bytes20(signer)).staticcall(
abi.encodeCall(IERC7913SignatureVerifier.verify, (signer.slice(20), hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC7913SignatureVerifier.verify.selector));
}
}

/**
* @dev Verifies multiple ERC-7913 `signatures` for a given `hash` using a set of `signers`.
* Returns `false` if the number of signers and signatures is not the same.
*
* The signers should be ordered by their `keccak256` hash to ensure efficient duplication check. Unordered
* signers are supported, but the uniqueness check will be more expensive.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function areValidERC7913SignaturesNow(
bytes32 hash,
bytes[] memory signers,
bytes[] memory signatures
) internal view returns (bool) {
if (signers.length != signatures.length) return false;

bytes32 lastId = bytes32(0);

for (uint256 i = 0; i < signers.length; ++i) {
bytes memory signer = signers[i];

// If one of the signatures is invalid, reject the batch
if (!isValidERC7913SignatureNow(signer, hash, signatures[i])) return false;

bytes32 id = keccak256(signer);
// If the current signer ID is greater than all previous IDs, then this is a new signer.
if (lastId < id) {
lastId = id;
} else {
// If this signer id is not greater than all the previous ones, verify that it is not a duplicate of a previous one
// This loop is never executed if the signers are ordered by id.
for (uint256 j = 0; j < i; ++j) {
if (id == keccak256(signers[j])) return false;
}
}
}

return true;
}
}
Loading