@@ -4,25 +4,32 @@ import { CachedEIP5719 } from '@0xsequence/replacer'
4
4
import { ethers } from 'ethers'
5
5
import { ConfigTracker , PresignedConfig , PresignedConfigLink } from '../tracker'
6
6
7
- const RATE_LIMIT_RETRY_DELAY = 5 * 60 * 1000
8
-
9
7
// depending on @0xsequence/abi breaks 0xsequence's proxy-transport-channel integration test
10
8
const MAIN_MODULE_ABI = [
11
9
'function execute((bool delegateCall, bool revertOnError, uint256 gasLimit, address target, uint256 value, bytes data)[] calldata transactions, uint256 nonce, bytes calldata signature)'
12
10
]
13
11
12
+ export interface Options {
13
+ readonly namespace ?: string
14
+ readonly owners ?: string [ ]
15
+ readonly eip5719Provider ?: ethers . Provider
16
+ readonly rateLimitRetryDelayMs ?: number
17
+ }
18
+
19
+ export const defaults = {
20
+ namespace : 'Sequence-Sessions' ,
21
+ owners : [ 'AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM' ] ,
22
+ eip5719Provider : undefined ,
23
+ rateLimitRetryDelayMs : 5 * 60 * 1000
24
+ }
25
+
14
26
export class ArweaveReader implements ConfigTracker , migrator . PresignedMigrationTracker {
15
27
private readonly configs : Map < string , Promise < commons . config . Config | undefined > > = new Map ( )
16
-
17
28
private readonly eip5719 ?: CachedEIP5719
18
29
19
- constructor (
20
- readonly namespace = 'Sequence-Sessions' ,
21
- readonly owners ?: string [ ] ,
22
- eip5719Provider ?: ethers . Provider
23
- ) {
24
- if ( eip5719Provider ) {
25
- this . eip5719 = new CachedEIP5719 ( eip5719Provider )
30
+ constructor ( readonly options : Options = defaults ) {
31
+ if ( options . eip5719Provider ) {
32
+ this . eip5719 = new CachedEIP5719 ( options . eip5719Provider )
26
33
}
27
34
}
28
35
@@ -42,62 +49,62 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
42
49
}
43
50
const fromCheckpoint = BigInt ( fromConfig . checkpoint )
44
51
45
- const items = Object . entries (
46
- await findItems ( { Type : 'config update' , Wallet : wallet } , { namespace : this . namespace , owners : this . owners } )
47
- ) . flatMap ( ( [ id , tags ] ) => {
48
- try {
49
- const { Signer : signer , Subdigest : subdigest , Digest : digest , 'To-Config' : toImageHash } = tags
52
+ const items = Object . entries ( await findItems ( { Type : 'config update' , Wallet : wallet } , this . options ) ) . flatMap (
53
+ ( [ id , tags ] ) => {
54
+ try {
55
+ const { Signer : signer , Subdigest : subdigest , Digest : digest , 'To-Config' : toImageHash } = tags
56
+
57
+ let signatureType : 'eip-712' | 'eth_sign' | 'erc-1271'
58
+ switch ( tags [ 'Signature-Type' ] ) {
59
+ case 'eip-712' :
60
+ case 'eth_sign' :
61
+ case 'erc-1271' :
62
+ signatureType = tags [ 'Signature-Type' ]
63
+ break
64
+ default :
65
+ throw new Error ( `unknown signature type ${ tags [ 'Signature-Type' ] } ` )
66
+ }
50
67
51
- let signatureType : 'eip-712' | 'eth_sign' | 'erc-1271'
52
- switch ( tags [ 'Signature-Type' ] ) {
53
- case 'eip-712' :
54
- case 'eth_sign' :
55
- case 'erc-1271' :
56
- signatureType = tags [ 'Signature-Type' ]
57
- break
58
- default :
59
- throw new Error ( `unknown signature type ${ tags [ 'Signature-Type' ] } ` )
60
- }
68
+ let toCheckpoint : bigint
69
+ try {
70
+ toCheckpoint = BigInt ( tags [ 'To-Checkpoint' ] )
71
+ } catch {
72
+ throw new Error ( `to checkpoint is not a number: ${ tags [ 'To-Checkpoint' ] } ` )
73
+ }
74
+ if ( toCheckpoint <= fromCheckpoint ) {
75
+ return [ ]
76
+ }
61
77
62
- let toCheckpoint : bigint
63
- try {
64
- toCheckpoint = BigInt ( tags [ 'To-Checkpoint' ] )
65
- } catch {
66
- throw new Error ( `to checkpoint is not a number: ${ tags [ 'To-Checkpoint' ] } ` )
67
- }
68
- if ( toCheckpoint <= fromCheckpoint ) {
69
- return [ ]
70
- }
78
+ if ( ! ethers . isAddress ( signer ) ) {
79
+ throw new Error ( `signer is not an address: ${ signer } ` )
80
+ }
71
81
72
- if ( ! ethers . isAddress ( signer ) ) {
73
- throw new Error ( `signer is not an address : ${ signer } ` )
74
- }
82
+ if ( ! ethers . isHexString ( subdigest , 32 ) ) {
83
+ throw new Error ( `subdigest is not a hash : ${ subdigest } ` )
84
+ }
75
85
76
- if ( ! ethers . isHexString ( subdigest , 32 ) ) {
77
- throw new Error ( `subdigest is not a hash: ${ subdigest } ` )
78
- }
86
+ if ( ! ethers . isHexString ( digest , 32 ) ) {
87
+ throw new Error ( `digest is not a hash: ${ digest } ` )
88
+ }
79
89
80
- if ( ! ethers . isHexString ( digest , 32 ) ) {
81
- throw new Error ( `digest is not a hash: ${ digest } ` )
82
- }
90
+ let chainId : bigint
91
+ try {
92
+ chainId = BigInt ( tags [ 'Chain-ID' ] )
93
+ } catch {
94
+ throw new Error ( `chain id is not a number: ${ tags [ 'Chain-ID' ] } ` )
95
+ }
83
96
84
- let chainId : bigint
85
- try {
86
- chainId = BigInt ( tags [ 'Chain-ID' ] )
87
- } catch {
88
- throw new Error ( `chain id is not a number: ${ tags [ 'Chain-ID' ] } ` )
89
- }
97
+ if ( ! ethers . isHexString ( toImageHash , 32 ) ) {
98
+ throw new Error ( `to config is not a hash: ${ toImageHash } ` )
99
+ }
90
100
91
- if ( ! ethers . isHexString ( toImageHash , 32 ) ) {
92
- throw new Error ( `to config is not a hash: ${ toImageHash } ` )
101
+ return [ { id, signatureType, signer, subdigest, digest, chainId, toImageHash, toCheckpoint } ]
102
+ } catch ( error ) {
103
+ console . warn ( `invalid wallet ${ wallet } config update ${ id } :` , error )
104
+ return [ ]
93
105
}
94
-
95
- return [ { id, signatureType, signer, subdigest, digest, chainId, toImageHash, toCheckpoint } ]
96
- } catch ( error ) {
97
- console . warn ( `invalid wallet ${ wallet } config update ${ id } :` , error )
98
- return [ ]
99
106
}
100
- } )
107
+ )
101
108
102
109
const signatures : Map < string , Map < string , ( typeof items ) [ number ] > > = new Map ( )
103
110
let candidates : typeof items = [ ]
@@ -166,7 +173,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
166
173
nextCandidateSigners . map ( async signer => {
167
174
const { id, subdigest, signatureType } = nextCandidateItems . get ( signer ) !
168
175
try {
169
- let signature = await ( await fetchItem ( id ) ) . text ( )
176
+ let signature = await ( await fetchItem ( id , this . options . rateLimitRetryDelayMs ) ) . text ( )
170
177
switch ( signatureType ) {
171
178
case 'eip-712' :
172
179
signature += '01'
@@ -240,9 +247,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
240
247
}
241
248
242
249
const config = ( async ( imageHash : string ) : Promise < commons . config . Config | undefined > => {
243
- const items = Object . entries (
244
- await findItems ( { Type : 'config' , Config : imageHash } , { namespace : this . namespace , owners : this . owners } )
245
- ) . flatMap ( ( [ id , tags ] ) => {
250
+ const items = Object . entries ( await findItems ( { Type : 'config' , Config : imageHash } , this . options ) ) . flatMap ( ( [ id , tags ] ) => {
246
251
try {
247
252
const version = Number ( tags . Version )
248
253
if ( ! version ) {
@@ -269,7 +274,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
269
274
270
275
for ( const { id, version } of items ) {
271
276
try {
272
- const config = { ...( await ( await fetchItem ( id ) ) . json ( ) ) , version }
277
+ const config = { ...( await ( await fetchItem ( id , this . options . rateLimitRetryDelayMs ) ) . json ( ) ) , version }
273
278
if ( config . tree ) {
274
279
config . tree = toTopology ( config . tree )
275
280
}
@@ -307,9 +312,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
307
312
} ) : Promise < { imageHash : string ; context : commons . context . WalletContext } | undefined > {
308
313
const wallet = ethers . getAddress ( args . wallet )
309
314
310
- const items = Object . entries (
311
- await findItems ( { Type : 'wallet' , Wallet : wallet } , { namespace : this . namespace , owners : this . owners } )
312
- ) . flatMap ( ( [ id , tags ] ) => {
315
+ const items = Object . entries ( await findItems ( { Type : 'wallet' , Wallet : wallet } , this . options ) ) . flatMap ( ( [ id , tags ] ) => {
313
316
try {
314
317
const { 'Deploy-Config' : imageHash } = tags
315
318
@@ -367,7 +370,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
367
370
for ( const [ id , tags ] of Object . entries (
368
371
await findItems (
369
372
{ Type : [ 'signature' , 'config update' ] , Signer : signer , Witness : args . allSignatures ? undefined : 'true' } ,
370
- { namespace : this . namespace , owners : this . owners }
373
+ this . options
371
374
)
372
375
) ) {
373
376
const { Wallet : wallet , Subdigest : subdigest , Digest : digest , 'Chain-ID' : chainId } = tags
@@ -396,7 +399,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
396
399
throw new Error ( 'incorrect subdigest' )
397
400
}
398
401
399
- const signature = fetchItem ( id ) . then ( async response => {
402
+ const signature = fetchItem ( id , this . options . rateLimitRetryDelayMs ) . then ( async response => {
400
403
const signature = ( await response . text ( ) ) + signatureType
401
404
if ( this . eip5719 ) {
402
405
try {
@@ -439,7 +442,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
439
442
'From-Version' : `${ fromVersion } ` ,
440
443
'From-Config' : fromImageHash
441
444
} ,
442
- { namespace : this . namespace , owners : this . owners }
445
+ this . options
443
446
)
444
447
) . flatMap ( ( [ id , tags ] ) => {
445
448
try {
@@ -483,7 +486,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
483
486
const { id, toVersion, toImageHash, executor } = items [ 0 ]
484
487
485
488
const [ data , toConfig ] = await Promise . all ( [
486
- fetchItem ( id ) . then ( response => response . text ( ) ) ,
489
+ fetchItem ( id , this . options . rateLimitRetryDelayMs ) . then ( response => response . text ( ) ) ,
487
490
this . configOfImageHash ( { imageHash : toImageHash } )
488
491
] )
489
492
@@ -511,10 +514,11 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
511
514
512
515
async function findItems (
513
516
filter : { [ name : string ] : undefined | string | string [ ] } ,
514
- options ?: { namespace ?: string ; owners ?: string [ ] ; pageSize ?: number ; maxResults ?: number }
517
+ options ?: Options & { pageSize ?: number ; maxResults ?: number }
515
518
) : Promise < { [ id : string ] : { [ tag : string ] : string } } > {
516
519
const namespace = options ?. namespace
517
520
const owners = options ?. owners
521
+ const rateLimitRetryDelayMs = options ?. rateLimitRetryDelayMs ?? defaults . rateLimitRetryDelayMs
518
522
const pageSize = options ?. pageSize ?? 100
519
523
const maxResults = options ?. maxResults
520
524
@@ -561,9 +565,9 @@ async function findItems(
561
565
break
562
566
}
563
567
console . warn (
564
- `rate limited by arweave.net, trying again in ${ RATE_LIMIT_RETRY_DELAY / 1000 } seconds at ${ new Date ( Date . now ( ) + RATE_LIMIT_RETRY_DELAY ) . toLocaleTimeString ( ) } `
568
+ `rate limited by arweave.net, trying again in ${ rateLimitRetryDelayMs / 1000 } seconds at ${ new Date ( Date . now ( ) + rateLimitRetryDelayMs ) . toLocaleTimeString ( ) } `
565
569
)
566
- await new Promise ( resolve => setTimeout ( resolve , RATE_LIMIT_RETRY_DELAY ) )
570
+ await new Promise ( resolve => setTimeout ( resolve , rateLimitRetryDelayMs ) )
567
571
}
568
572
569
573
const {
@@ -588,16 +592,16 @@ async function findItems(
588
592
)
589
593
}
590
594
591
- async function fetchItem ( id : string ) : Promise < Response > {
595
+ async function fetchItem ( id : string , rateLimitRetryDelayMs = defaults . rateLimitRetryDelayMs ) : Promise < Response > {
592
596
while ( true ) {
593
597
const response = await fetch ( `https://arweave.net/${ id } ` , { redirect : 'follow' } )
594
598
if ( response . status !== 429 ) {
595
599
return response
596
600
}
597
601
console . warn (
598
- `rate limited by arweave.net, trying again in ${ RATE_LIMIT_RETRY_DELAY / 1000 } seconds at ${ new Date ( Date . now ( ) + RATE_LIMIT_RETRY_DELAY ) . toLocaleTimeString ( ) } `
602
+ `rate limited by arweave.net, trying again in ${ rateLimitRetryDelayMs / 1000 } seconds at ${ new Date ( Date . now ( ) + rateLimitRetryDelayMs ) . toLocaleTimeString ( ) } `
599
603
)
600
- await new Promise ( resolve => setTimeout ( resolve , RATE_LIMIT_RETRY_DELAY ) )
604
+ await new Promise ( resolve => setTimeout ( resolve , rateLimitRetryDelayMs ) )
601
605
}
602
606
}
603
607
0 commit comments