@@ -24,9 +24,9 @@ async function fixture() {
24
24
const newSigner = ethers . Wallet . createRandom ( ) ;
25
25
26
26
// ERC-4337 account
27
- const helper = new ERC4337Helper ( ) ;
28
- const env = await helper . wait ( ) ;
29
- const accountMock = await helper . newAccount ( '$AccountERC7579Mock' , [
27
+ const erc4337Helper = new ERC4337Helper ( ) ;
28
+ const env = await erc4337Helper . wait ( ) ;
29
+ const accountMock = await erc4337Helper . newAccount ( '$AccountERC7579Mock' , [
30
30
'AccountERC7579' ,
31
31
'1' ,
32
32
validatorMock . target ,
@@ -55,16 +55,16 @@ async function fixture() {
55
55
// impersonate ERC-4337 Canonical Entrypoint
56
56
const accountMockFromEntrypoint = accountMock . connect ( await impersonate ( entrypoint . target ) ) ;
57
57
58
+ // ERC-7579 Social Recovery Executor Module
59
+ const mock = await ethers . deployContract ( '$SocialRecoveryExecutor' , [ 'SocialRecoveryExecutor' , '0.0.1' ] ) ;
60
+
58
61
// ERC-7579 Social Recovery Executor Module Initial Config
59
62
const recoveryConfig = {
60
63
guardians : new Array ( 3 ) . fill ( null ) . map ( ( ) => ethers . Wallet . createRandom ( ) ) ,
61
64
threshold : 2 ,
62
65
timelock : time . duration . days ( 1 ) ,
63
66
} ;
64
67
65
- // ERC-7579 Social Recovery Executor Module
66
- const mock = await ethers . deployContract ( '$SocialRecoveryExecutor' , [ 'SocialRecoveryExecutor' , '0.0.1' ] ) ;
67
-
68
68
return {
69
69
...env ,
70
70
validatorMock,
@@ -149,14 +149,33 @@ describe('SocialRecoveryExecutorModule', function () {
149
149
const message = 'Hello Social Recovery' ;
150
150
const digest = ethers . hashMessage ( message ) ;
151
151
152
- const guardianSignatures = invalidGuardians . map ( g => ( {
152
+ const guardianSignatures = SocialRecoveryExecutorHelper . sortGuardianSignatures (
153
+ invalidGuardians . map ( g => ( {
154
+ signer : g . address ,
155
+ signature : g . signMessage ( message ) ,
156
+ } ) ) ,
157
+ ) ;
158
+
159
+ await expect (
160
+ this . mock . validateGuardianSignatures ( this . accountMock . target , guardianSignatures , digest ) ,
161
+ ) . to . be . revertedWithCustomError ( this . mock , 'InvalidGuardianSignature' ) ;
162
+ } ) ;
163
+
164
+ it ( 'should invalidate unsorted guardian signatures' , async function ( ) {
165
+ const guardians = this . recoveryConfig . guardians . slice ( 0 , 2 ) ;
166
+ const reversedGuardians = guardians . sort ( ) . reverse ( ) ;
167
+
168
+ const message = 'Hello Social Recovery' ;
169
+ const digest = ethers . hashMessage ( message ) ;
170
+
171
+ const guardianSignatures = reversedGuardians . map ( g => ( {
153
172
signer : g . address ,
154
173
signature : g . signMessage ( message ) ,
155
174
} ) ) ;
156
175
157
176
await expect (
158
177
this . mock . validateGuardianSignatures ( this . accountMock . target , guardianSignatures , digest ) ,
159
- ) . to . be . revertedWithCustomError ( this . mock , 'InvalidGuardianSignature ' ) ;
178
+ ) . to . be . revertedWithCustomError ( this . mock , 'DuplicatedOrUnsortedGuardianSignatures ' ) ;
160
179
} ) ;
161
180
162
181
it ( 'should invalidate duplicate guardian signatures' , async function ( ) {
@@ -166,25 +185,29 @@ describe('SocialRecoveryExecutorModule', function () {
166
185
const message = 'Hello Social Recovery' ;
167
186
const digest = ethers . hashMessage ( message ) ;
168
187
169
- const guardianSignatures = identicalGuardians . map ( g => ( {
170
- signer : g . address ,
171
- signature : g . signMessage ( message ) ,
172
- } ) ) ;
188
+ const guardianSignatures = SocialRecoveryExecutorHelper . sortGuardianSignatures (
189
+ identicalGuardians . map ( g => ( {
190
+ signer : g . address ,
191
+ signature : g . signMessage ( message ) ,
192
+ } ) ) ,
193
+ ) ;
173
194
174
195
await expect (
175
196
this . mock . validateGuardianSignatures ( this . accountMock . target , guardianSignatures , digest ) ,
176
- ) . to . be . revertedWithCustomError ( this . mock , 'DuplicateGuardianSignature ' ) ;
197
+ ) . to . be . revertedWithCustomError ( this . mock , 'DuplicatedOrUnsortedGuardianSignatures ' ) ;
177
198
} ) ;
178
199
179
200
it ( 'should fail if threshold is not met' , async function ( ) {
180
201
const insufficientGuardians = this . recoveryConfig . guardians . slice ( 0 , this . recoveryConfig . threshold - 1 ) ;
181
202
const message = 'Hello Social Recovery' ;
182
203
const digest = ethers . hashMessage ( message ) ;
183
204
184
- const guardianSignatures = insufficientGuardians . map ( g => ( {
185
- signer : g . address ,
186
- signature : g . signMessage ( message ) ,
187
- } ) ) ;
205
+ const guardianSignatures = SocialRecoveryExecutorHelper . sortGuardianSignatures (
206
+ insufficientGuardians . map ( g => ( {
207
+ signer : g . address ,
208
+ signature : g . signMessage ( message ) ,
209
+ } ) ) ,
210
+ ) ;
188
211
189
212
await expect (
190
213
this . mock . validateGuardianSignatures ( this . accountMock . target , guardianSignatures , digest ) ,
@@ -196,10 +219,12 @@ describe('SocialRecoveryExecutorModule', function () {
196
219
const message = 'Hello Social Recovery' ;
197
220
const digest = ethers . hashMessage ( message ) ;
198
221
199
- const guardianSignatures = guardians . map ( g => ( {
200
- signer : g . address ,
201
- signature : g . signMessage ( message ) ,
202
- } ) ) ;
222
+ const guardianSignatures = SocialRecoveryExecutorHelper . sortGuardianSignatures (
223
+ guardians . map ( g => ( {
224
+ signer : g . address ,
225
+ signature : g . signMessage ( message ) ,
226
+ } ) ) ,
227
+ ) ;
203
228
204
229
await expect ( this . mock . validateGuardianSignatures ( this . accountMock . target , guardianSignatures , digest ) ) . to . not
205
230
. be . reverted ;
@@ -232,10 +257,12 @@ describe('SocialRecoveryExecutorModule', function () {
232
257
executionCalldata : executionCalldata ,
233
258
} ;
234
259
235
- const guardianSignatures = guardians . map ( g => ( {
236
- signer : g . address ,
237
- signature : g . signTypedData ( domain , SocialRecoveryExecutorHelper . START_RECOVERY_TYPEHASH , message ) ,
238
- } ) ) ;
260
+ const guardianSignatures = SocialRecoveryExecutorHelper . sortGuardianSignatures (
261
+ guardians . map ( g => ( {
262
+ signer : g . address ,
263
+ signature : g . signTypedData ( domain , SocialRecoveryExecutorHelper . START_RECOVERY_TYPEHASH , message ) ,
264
+ } ) ) ,
265
+ ) ;
239
266
240
267
await expect ( this . mock . startRecovery ( this . accountMock . target , guardianSignatures , executionCalldata ) )
241
268
. to . emit ( this . mock , 'RecoveryStarted' )
@@ -353,10 +380,12 @@ describe('SocialRecoveryExecutorModule', function () {
353
380
nonce : await this . mock . nonces ( this . accountMock . target ) ,
354
381
} ;
355
382
356
- const guardianSignatures = guardians . map ( g => ( {
357
- signer : g . address ,
358
- signature : g . signTypedData ( domain , SocialRecoveryExecutorHelper . CANCEL_RECOVERY_TYPEHASH , message ) ,
359
- } ) ) ;
383
+ const guardianSignatures = SocialRecoveryExecutorHelper . sortGuardianSignatures (
384
+ guardians . map ( g => ( {
385
+ signer : g . address ,
386
+ signature : g . signTypedData ( domain , SocialRecoveryExecutorHelper . CANCEL_RECOVERY_TYPEHASH , message ) ,
387
+ } ) ) ,
388
+ ) ;
360
389
361
390
await expect ( this . mock . cancelRecovery ( this . accountMock . target , guardianSignatures ) )
362
391
. to . emit ( this . mock , 'RecoveryCancelled' )
@@ -418,10 +447,12 @@ describe('SocialRecoveryExecutorModule', function () {
418
447
executionCalldata : executionCalldata ,
419
448
} ;
420
449
421
- const guardianSignatures = guardians . map ( g => ( {
422
- signer : g . address ,
423
- signature : g . signTypedData ( domain , SocialRecoveryExecutorHelper . START_RECOVERY_TYPEHASH , message ) ,
424
- } ) ) ;
450
+ const guardianSignatures = SocialRecoveryExecutorHelper . sortGuardianSignatures (
451
+ guardians . map ( g => ( {
452
+ signer : g . address ,
453
+ signature : g . signTypedData ( domain , SocialRecoveryExecutorHelper . START_RECOVERY_TYPEHASH , message ) ,
454
+ } ) ) ,
455
+ ) ;
425
456
426
457
await expect (
427
458
this . mock . startRecovery ( this . accountMock . target , guardianSignatures , executionCalldata ) ,
0 commit comments