Skip to content

Commit 0aedfa2

Browse files
committed
Merge branch 'master' of github.com:gonzaotc/openzeppelin-community-contracts into feature/SocialRecoveryExecutor
2 parents 9186146 + 7c47069 commit 0aedfa2

18 files changed

+296
-280
lines changed

FUNDING.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"drips": {
3+
"ethereum": {
4+
"ownedBy": "0xAeb37910f93486C85A1F8F994b67E8187554d664"
5+
},
6+
},
7+
"opRetro": {
8+
"projectId": "0x939241afa4c4b9e1dda6b8250baa8f04fa8b0debce738cfd324c0b18f9926d25"
9+
}
10+
}

contracts/account/Account.sol

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
pragma solidity ^0.8.20;
44

55
import {PackedUserOperation} from "@openzeppelin/contracts/interfaces/draft-IERC4337.sol";
6-
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
76
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
87
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
98
import {ERC7739} from "../utils/cryptography/ERC7739.sol";
@@ -21,8 +20,8 @@ import {AccountCore} from "./AccountCore.sol";
2120
* NOTE: To use this contract, the {ERC7739-_rawSignatureValidation} function must be
2221
* implemented using a specific signature verification algorithm. See {SignerECDSA}, {SignerP256} or {SignerRSA}.
2322
*/
24-
abstract contract Account is AccountCore, EIP712, ERC721Holder, ERC1155Holder, ERC7739 {
25-
bytes32 internal constant _PACKED_USER_OPERATION =
23+
abstract contract Account is AccountCore, ERC721Holder, ERC1155Holder, ERC7739 {
24+
bytes32 private constant PACKED_USER_OPERATION =
2625
keccak256(
2726
"PackedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterAndData)"
2827
);
@@ -40,7 +39,7 @@ abstract contract Account is AccountCore, EIP712, ERC721Holder, ERC1155Holder, E
4039
_hashTypedDataV4(
4140
keccak256(
4241
abi.encode(
43-
_PACKED_USER_OPERATION,
42+
PACKED_USER_OPERATION,
4443
userOp.sender,
4544
userOp.nonce,
4645
keccak256(userOp.initCode),

contracts/account/extensions/AccountERC7579.sol

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {Bytes} from "@openzeppelin/contracts/utils/Bytes.sol";
1111
import {Packing} from "@openzeppelin/contracts/utils/Packing.sol";
1212
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
1313
import {Calldata} from "@openzeppelin/contracts/utils/Calldata.sol";
14-
import {ERC7739} from "../../utils/cryptography/ERC7739.sol";
1514
import {AccountCore} from "../AccountCore.sol";
1615

1716
/**
@@ -43,7 +42,7 @@ import {AccountCore} from "../AccountCore.sol";
4342
*/
4443
abstract contract AccountERC7579 is
4544
AccountCore,
46-
ERC7739,
45+
IERC1271,
4746
IERC7579Execution,
4847
IERC7579AccountConfig,
4948
IERC7579ModuleConfig
@@ -145,12 +144,12 @@ abstract contract AccountERC7579 is
145144
return false;
146145
}
147146

148-
/// @dev Executes a transaction from the entry point or the account itself. See {_execute}.
147+
/// @inheritdoc IERC7579Execution
149148
function execute(bytes32 mode, bytes calldata executionCalldata) public payable virtual onlyEntryPointOrSelf {
150149
_execute(Mode.wrap(mode), executionCalldata);
151150
}
152151

153-
/// @dev Executes a transaction from the executor module. See {_execute}.
152+
/// @inheritdoc IERC7579Execution
154153
function executeFromExecutor(
155154
bytes32 mode,
156155
bytes calldata executionCalldata
@@ -164,6 +163,30 @@ abstract contract AccountERC7579 is
164163
return _execute(Mode.wrap(mode), executionCalldata);
165164
}
166165

166+
/**
167+
* @dev Implement ERC-1271 through IERC7579Validator modules. If module based validation fails, fallback to
168+
* "native" validation by the abstract signer.
169+
*
170+
* NOTE: when combined with {ERC7739} (for example through {Account}), resolution ordering may have an impact
171+
* ({ERC7739} does not call super). Manual resolution might be necessary.
172+
*/
173+
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual override returns (bytes4) {
174+
// check signature length is enough for extraction
175+
if (signature.length >= 20) {
176+
(address module, bytes calldata innerSignature) = _extractSignatureValidator(signature);
177+
// if module is not installed, skip
178+
if (isModuleInstalled(MODULE_TYPE_VALIDATOR, module, Calldata.emptyBytes())) {
179+
// try validation, skip any revert
180+
try IERC7579Validator(module).isValidSignatureWithSender(address(this), hash, innerSignature) returns (
181+
bytes4 magic
182+
) {
183+
if (magic == IERC1271.isValidSignature.selector) return magic;
184+
} catch {}
185+
}
186+
}
187+
return bytes4(0xffffffff);
188+
}
189+
167190
/**
168191
* @dev Validates a user operation with {_signableUserOpHash} and returns the validation data
169192
* if the module specified by the first 20 bytes of the nonce key is installed. Falls back to
@@ -182,26 +205,6 @@ abstract contract AccountERC7579 is
182205
: super._validateUserOp(userOp, userOpHash);
183206
}
184207

185-
/**
186-
* @dev Lowest-level signature validation function. See {ERC7739-_rawSignatureValidation}.
187-
*
188-
* This function delegates the signature validation to a validation module if the first 20 bytes of the
189-
* signature correspond to an installed validator module.
190-
*
191-
* See {_extractSignatureValidator} for the module extraction logic.
192-
*/
193-
function _rawSignatureValidation(
194-
bytes32 hash,
195-
bytes calldata signature
196-
) internal view virtual override returns (bool) {
197-
if (signature.length < 20) return false;
198-
(address module, bytes calldata innerSignature) = _extractSignatureValidator(signature);
199-
return
200-
isModuleInstalled(MODULE_TYPE_VALIDATOR, module, Calldata.emptyBytes()) &&
201-
IERC7579Validator(module).isValidSignatureWithSender(address(this), hash, innerSignature) ==
202-
IERC1271.isValidSignature.selector;
203-
}
204-
205208
/**
206209
* @dev ERC-7579 execution logic. See {supportsExecutionMode} for supported modes.
207210
*
@@ -387,4 +390,12 @@ abstract contract AccountERC7579 is
387390
) internal pure virtual returns (bytes4 selector, bytes memory remaining) {
388391
return (bytes4(data), data.slice(4));
389392
}
393+
394+
/// @dev By default, only use the modules for validation of userOp and signature. Disable raw signatures.
395+
function _rawSignatureValidation(
396+
bytes32 /*hash*/,
397+
bytes calldata /*signature*/
398+
) internal view virtual override returns (bool) {
399+
return false;
400+
}
390401
}

contracts/mocks/account/AccountERC7702WithModulesMock.sol

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {Account} from "../../account/Account.sol";
88
import {AccountERC7579} from "../../account/extensions/AccountERC7579.sol";
99
import {ERC7821} from "../../account/extensions/ERC7821.sol";
1010
import {AbstractSigner} from "../../utils/cryptography/AbstractSigner.sol";
11+
import {ERC7739} from "../../utils/cryptography/ERC7739.sol";
1112
import {SignerERC7702} from "../../utils/cryptography/SignerERC7702.sol";
1213

1314
abstract contract AccountERC7702WithModulesMock is Account, AccountERC7579, SignerERC7702 {
@@ -18,13 +19,22 @@ abstract contract AccountERC7702WithModulesMock is Account, AccountERC7579, Sign
1819
return super._validateUserOp(userOp, userOpHash);
1920
}
2021

22+
/// @dev Resolve implementation of ERC-1271 by both ERC7739 and AccountERC7579 to support both schemes.
23+
function isValidSignature(
24+
bytes32 hash,
25+
bytes calldata signature
26+
) public view virtual override(ERC7739, AccountERC7579) returns (bytes4) {
27+
// ERC-7739 can return the fn selector (success), 0xffffffff (invalid) or 0x77390001 (detection).
28+
// If the return is 0xffffffff, we fallback to validation using ERC-7579 modules.
29+
bytes4 erc7739magic = ERC7739.isValidSignature(hash, signature);
30+
return erc7739magic == bytes4(0xffffffff) ? AccountERC7579.isValidSignature(hash, signature) : erc7739magic;
31+
}
32+
33+
/// @dev Enable signature using the ERC-7702 signer.
2134
function _rawSignatureValidation(
2235
bytes32 hash,
2336
bytes calldata signature
2437
) internal view virtual override(AbstractSigner, AccountERC7579, SignerERC7702) returns (bool) {
25-
// Try ERC-7702 first, and fallback to ERC-7579
26-
return
27-
SignerERC7702._rawSignatureValidation(hash, signature) ||
28-
AccountERC7579._rawSignatureValidation(hash, signature);
38+
return SignerERC7702._rawSignatureValidation(hash, signature);
2939
}
3040
}

contracts/mocks/account/AccountMock.sol

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@ import {Account} from "../../account/Account.sol";
99

1010
abstract contract AccountMock is Account, ERC7821 {
1111
/// Validates a user operation with a boolean signature.
12-
function _rawSignatureValidation(
13-
bytes32 /* userOpHash */,
14-
bytes calldata signature
15-
) internal pure override returns (bool) {
16-
return bytes1(signature[0:1]) == bytes1(0x01);
12+
function _rawSignatureValidation(bytes32 hash, bytes calldata signature) internal pure override returns (bool) {
13+
return signature.length >= 32 && bytes32(signature) == hash;
1714
}
1815

1916
/// @inheritdoc ERC7821

test/account/Account.test.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
const { ethers } = require('hardhat');
22
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
33
const { ERC4337Helper } = require('../helpers/erc4337');
4+
const { PackedUserOperation } = require('../helpers/eip712-types');
45
const { NonNativeSigner } = require('../helpers/signers');
56

67
const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior');
8+
const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior');
79
const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior');
810

911
async function fixture() {
@@ -12,19 +14,27 @@ async function fixture() {
1214
const target = await ethers.deployContract('CallReceiverMockExtended');
1315

1416
// ERC-4337 signer
15-
const signer = new NonNativeSigner({ sign: () => ({ serialized: '0x01' }) });
17+
const signer = new NonNativeSigner({ sign: hash => ({ serialized: hash }) });
1618

1719
// ERC-4337 account
1820
const helper = new ERC4337Helper();
1921
const env = await helper.wait();
2022
const mock = await helper.newAccount('$AccountMock', ['Account', '1']);
2123

22-
const signUserOp = async userOp => {
23-
userOp.signature = await signer.signMessage(userOp.hash());
24-
return userOp;
24+
// domain cannot be fetched using getDomain(mock) before the mock is deployed
25+
const domain = {
26+
name: 'Account',
27+
version: '1',
28+
chainId: env.chainId,
29+
verifyingContract: mock.address,
2530
};
2631

27-
return { ...env, mock, signer, target, beneficiary, other, signUserOp };
32+
const signUserOp = async userOp =>
33+
signer
34+
.signTypedData(domain, { PackedUserOperation }, userOp.packed)
35+
.then(signature => Object.assign(userOp, { signature }));
36+
37+
return { ...env, mock, domain, signer, target, beneficiary, other, signUserOp };
2838
}
2939

3040
describe('Account', function () {
@@ -34,5 +44,6 @@ describe('Account', function () {
3444

3545
shouldBehaveLikeAccountCore();
3646
shouldBehaveLikeAccountHolder();
47+
shouldBehaveLikeERC1271({ erc7739: true });
3748
shouldBehaveLikeERC7821();
3849
});

test/account/AccountECDSA.test.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { ERC4337Helper } = require('../helpers/erc4337');
44
const { PackedUserOperation } = require('../helpers/eip712-types');
55

66
const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior');
7-
const { shouldBehaveLikeERC7739 } = require('../utils/cryptography/ERC7739.behavior');
7+
const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior');
88
const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior');
99

1010
async function fixture() {
@@ -43,13 +43,6 @@ describe('AccountECDSA', function () {
4343

4444
shouldBehaveLikeAccountCore();
4545
shouldBehaveLikeAccountHolder();
46+
shouldBehaveLikeERC1271({ erc7739: true });
4647
shouldBehaveLikeERC7821();
47-
48-
describe('ERC7739', function () {
49-
beforeEach(async function () {
50-
this.mock = await this.mock.deploy();
51-
});
52-
53-
shouldBehaveLikeERC7739();
54-
});
5548
});

test/account/AccountERC7702.test.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { ERC4337Helper } = require('../helpers/erc4337');
44
const { PackedUserOperation } = require('../helpers/eip712-types');
55

66
const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior');
7-
const { shouldBehaveLikeERC7739 } = require('../utils/cryptography/ERC7739.behavior');
7+
const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior');
88
const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior');
99

1010
async function fixture() {
@@ -43,13 +43,6 @@ describe('AccountERC7702', function () {
4343

4444
shouldBehaveLikeAccountCore();
4545
shouldBehaveLikeAccountHolder();
46+
shouldBehaveLikeERC1271({ erc7739: true });
4647
shouldBehaveLikeERC7821({ deployable: false });
47-
48-
describe('ERC7739', function () {
49-
beforeEach(async function () {
50-
this.mock = await this.mock.deploy();
51-
});
52-
53-
shouldBehaveLikeERC7739();
54-
});
5548
});

test/account/AccountP256.test.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { NonNativeSigner, P256SigningKey } = require('../helpers/signers');
55
const { PackedUserOperation } = require('../helpers/eip712-types');
66

77
const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior');
8-
const { shouldBehaveLikeERC7739 } = require('../utils/cryptography/ERC7739.behavior');
8+
const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior');
99
const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior');
1010

1111
async function fixture() {
@@ -49,13 +49,6 @@ describe('AccountP256', function () {
4949

5050
shouldBehaveLikeAccountCore();
5151
shouldBehaveLikeAccountHolder();
52+
shouldBehaveLikeERC1271({ erc7739: true });
5253
shouldBehaveLikeERC7821();
53-
54-
describe('ERC7739', function () {
55-
beforeEach(async function () {
56-
this.mock = await this.mock.deploy();
57-
});
58-
59-
shouldBehaveLikeERC7739();
60-
});
6154
});

test/account/AccountRSA.test.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { NonNativeSigner, RSASHA256SigningKey } = require('../helpers/signers');
55
const { PackedUserOperation } = require('../helpers/eip712-types');
66

77
const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior');
8-
const { shouldBehaveLikeERC7739 } = require('../utils/cryptography/ERC7739.behavior');
8+
const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior');
99
const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior');
1010

1111
async function fixture() {
@@ -49,13 +49,6 @@ describe('AccountRSA', function () {
4949

5050
shouldBehaveLikeAccountCore();
5151
shouldBehaveLikeAccountHolder();
52+
shouldBehaveLikeERC1271({ erc7739: true });
5253
shouldBehaveLikeERC7821();
53-
54-
describe('ERC7739', function () {
55-
beforeEach(async function () {
56-
this.mock = await this.mock.deploy();
57-
});
58-
59-
shouldBehaveLikeERC7739();
60-
});
6154
});

0 commit comments

Comments
 (0)