@feelyourprotocol/common 8141.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +409 -0
  3. package/dist/cjs/chains.d.ts +6 -0
  4. package/dist/cjs/chains.d.ts.map +1 -0
  5. package/dist/cjs/chains.js +637 -0
  6. package/dist/cjs/chains.js.map +1 -0
  7. package/dist/cjs/common.d.ts +318 -0
  8. package/dist/cjs/common.d.ts.map +1 -0
  9. package/dist/cjs/common.js +789 -0
  10. package/dist/cjs/common.js.map +1 -0
  11. package/dist/cjs/constructors.d.ts +27 -0
  12. package/dist/cjs/constructors.d.ts.map +1 -0
  13. package/dist/cjs/constructors.js +53 -0
  14. package/dist/cjs/constructors.js.map +1 -0
  15. package/dist/cjs/crc.d.ts +8 -0
  16. package/dist/cjs/crc.d.ts.map +1 -0
  17. package/dist/cjs/crc.js +63 -0
  18. package/dist/cjs/crc.js.map +1 -0
  19. package/dist/cjs/eips.d.ts +3 -0
  20. package/dist/cjs/eips.d.ts.map +1 -0
  21. package/dist/cjs/eips.js +589 -0
  22. package/dist/cjs/eips.js.map +1 -0
  23. package/dist/cjs/enums.d.ts +65 -0
  24. package/dist/cjs/enums.d.ts.map +1 -0
  25. package/dist/cjs/enums.js +80 -0
  26. package/dist/cjs/enums.js.map +1 -0
  27. package/dist/cjs/gethGenesis.d.ts +145 -0
  28. package/dist/cjs/gethGenesis.d.ts.map +1 -0
  29. package/dist/cjs/gethGenesis.js +26 -0
  30. package/dist/cjs/gethGenesis.js.map +1 -0
  31. package/dist/cjs/hardforks.d.ts +3 -0
  32. package/dist/cjs/hardforks.d.ts.map +1 -0
  33. package/dist/cjs/hardforks.js +207 -0
  34. package/dist/cjs/hardforks.js.map +1 -0
  35. package/dist/cjs/index.d.ts +9 -0
  36. package/dist/cjs/index.d.ts.map +1 -0
  37. package/dist/cjs/index.js +25 -0
  38. package/dist/cjs/index.js.map +1 -0
  39. package/dist/cjs/interfaces.d.ts +132 -0
  40. package/dist/cjs/interfaces.d.ts.map +1 -0
  41. package/dist/cjs/interfaces.js +13 -0
  42. package/dist/cjs/interfaces.js.map +1 -0
  43. package/dist/cjs/package.json +3 -0
  44. package/dist/cjs/types.d.ts +167 -0
  45. package/dist/cjs/types.d.ts.map +1 -0
  46. package/dist/cjs/types.js +3 -0
  47. package/dist/cjs/types.js.map +1 -0
  48. package/dist/cjs/utils.d.ts +60 -0
  49. package/dist/cjs/utils.d.ts.map +1 -0
  50. package/dist/cjs/utils.js +263 -0
  51. package/dist/cjs/utils.js.map +1 -0
  52. package/dist/esm/chains.d.ts +6 -0
  53. package/dist/esm/chains.d.ts.map +1 -0
  54. package/dist/esm/chains.js +634 -0
  55. package/dist/esm/chains.js.map +1 -0
  56. package/dist/esm/common.d.ts +318 -0
  57. package/dist/esm/common.d.ts.map +1 -0
  58. package/dist/esm/common.js +785 -0
  59. package/dist/esm/common.js.map +1 -0
  60. package/dist/esm/constructors.d.ts +27 -0
  61. package/dist/esm/constructors.d.ts.map +1 -0
  62. package/dist/esm/constructors.js +49 -0
  63. package/dist/esm/constructors.js.map +1 -0
  64. package/dist/esm/crc.d.ts +8 -0
  65. package/dist/esm/crc.d.ts.map +1 -0
  66. package/dist/esm/crc.js +59 -0
  67. package/dist/esm/crc.js.map +1 -0
  68. package/dist/esm/eips.d.ts +3 -0
  69. package/dist/esm/eips.d.ts.map +1 -0
  70. package/dist/esm/eips.js +586 -0
  71. package/dist/esm/eips.js.map +1 -0
  72. package/dist/esm/enums.d.ts +65 -0
  73. package/dist/esm/enums.d.ts.map +1 -0
  74. package/dist/esm/enums.js +77 -0
  75. package/dist/esm/enums.js.map +1 -0
  76. package/dist/esm/gethGenesis.d.ts +145 -0
  77. package/dist/esm/gethGenesis.d.ts.map +1 -0
  78. package/dist/esm/gethGenesis.js +23 -0
  79. package/dist/esm/gethGenesis.js.map +1 -0
  80. package/dist/esm/hardforks.d.ts +3 -0
  81. package/dist/esm/hardforks.d.ts.map +1 -0
  82. package/dist/esm/hardforks.js +204 -0
  83. package/dist/esm/hardforks.js.map +1 -0
  84. package/dist/esm/index.d.ts +9 -0
  85. package/dist/esm/index.d.ts.map +1 -0
  86. package/dist/esm/index.js +9 -0
  87. package/dist/esm/index.js.map +1 -0
  88. package/dist/esm/interfaces.d.ts +132 -0
  89. package/dist/esm/interfaces.d.ts.map +1 -0
  90. package/dist/esm/interfaces.js +10 -0
  91. package/dist/esm/interfaces.js.map +1 -0
  92. package/dist/esm/package.json +3 -0
  93. package/dist/esm/types.d.ts +167 -0
  94. package/dist/esm/types.d.ts.map +1 -0
  95. package/dist/esm/types.js +2 -0
  96. package/dist/esm/types.js.map +1 -0
  97. package/dist/esm/utils.d.ts +60 -0
  98. package/dist/esm/utils.d.ts.map +1 -0
  99. package/dist/esm/utils.js +258 -0
  100. package/dist/esm/utils.js.map +1 -0
  101. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  102. package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
  103. package/package.json +77 -0
  104. package/src/chains.ts +638 -0
  105. package/src/common.ts +913 -0
  106. package/src/constructors.ts +60 -0
  107. package/src/crc.ts +63 -0
  108. package/src/eips.ts +588 -0
  109. package/src/enums.ts +104 -0
  110. package/src/gethGenesis.ts +175 -0
  111. package/src/hardforks.ts +205 -0
  112. package/src/index.ts +8 -0
  113. package/src/interfaces.ts +191 -0
  114. package/src/types.ts +193 -0
  115. package/src/utils.ts +324 -0
package/src/common.ts ADDED
@@ -0,0 +1,913 @@
1
+ import {
2
+ BIGINT_0,
3
+ EthereumJSErrorWithoutCode,
4
+ TypeOutput,
5
+ bytesToHex,
6
+ concatBytes,
7
+ hexToBytes,
8
+ intToBytes,
9
+ toType,
10
+ } from '@feelyourprotocol/util'
11
+ import { EventEmitter } from 'eventemitter3'
12
+
13
+ import { crc32 } from './crc.ts'
14
+ import { eipsDict } from './eips.ts'
15
+ import { Hardfork } from './enums.ts'
16
+ import { hardforksDict } from './hardforks.ts'
17
+
18
+ import type { BigIntLike, PrefixedHexString } from '@feelyourprotocol/util'
19
+ import type { ConsensusAlgorithm, ConsensusType } from './enums.ts'
20
+ import type {
21
+ BootstrapNodeConfig,
22
+ BpoSchedule,
23
+ CasperConfig,
24
+ ChainConfig,
25
+ CliqueConfig,
26
+ CommonEvent,
27
+ CommonOpts,
28
+ CustomCrypto,
29
+ EthashConfig,
30
+ GenesisBlockConfig,
31
+ HardforkByOpts,
32
+ HardforkConfig,
33
+ HardforkTransitionConfig,
34
+ ParamsConfig,
35
+ ParamsDict,
36
+ } from './types.ts'
37
+
38
+ /**
39
+ * Common class to access chain and hardfork parameters and to provide
40
+ * a unified and shared view on the network and hardfork state.
41
+ *
42
+ * Use the {@link createCustomCommon} constructor for creating simple
43
+ * custom chain {@link Common} objects (more complete custom chain setups
44
+ * can be created via the main constructor).
45
+ *
46
+ * Use the {@link createCommonFromGethGenesis} constructor for creating
47
+ * a Common object from a Geth genesis file.
48
+ */
49
+ export class Common {
50
+ readonly DEFAULT_HARDFORK: string | Hardfork
51
+
52
+ protected _chainParams: ChainConfig
53
+ protected _hardfork: string | Hardfork
54
+ protected _eips: number[] = []
55
+ protected _params: ParamsDict
56
+
57
+ public readonly customCrypto: CustomCrypto
58
+
59
+ protected _paramsCache: ParamsConfig = {}
60
+ protected _activatedEIPsCache: number[] = []
61
+
62
+ protected HARDFORK_CHANGES: [string, HardforkConfig][]
63
+
64
+ public events: EventEmitter<CommonEvent>
65
+
66
+ constructor(opts: CommonOpts) {
67
+ this.events = new EventEmitter<CommonEvent>()
68
+
69
+ this._chainParams = JSON.parse(JSON.stringify(opts.chain)) // copy
70
+ this.DEFAULT_HARDFORK = this._chainParams.defaultHardfork ?? Hardfork.Prague
71
+ // Assign hardfork changes in the sequence of the applied hardforks
72
+ this.HARDFORK_CHANGES = this.hardforks().map((hf) => [
73
+ hf.name,
74
+ // Allow to even override an existing hardfork specification
75
+ (this._chainParams.customHardforks && this._chainParams.customHardforks[hf.name]) ??
76
+ hardforksDict[hf.name],
77
+ ])
78
+ this._hardfork = this.DEFAULT_HARDFORK
79
+ this._params = opts.params ? JSON.parse(JSON.stringify(opts.params)) : {} // copy
80
+
81
+ if (opts.hardfork !== undefined) {
82
+ this.setHardfork(opts.hardfork)
83
+ }
84
+ if (opts.eips) {
85
+ this.setEIPs(opts.eips)
86
+ }
87
+ this.customCrypto = opts.customCrypto ?? {}
88
+
89
+ if (Object.keys(this._paramsCache).length === 0) {
90
+ this._buildParamsCache()
91
+ this._buildActivatedEIPsCache()
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Update the internal Common EIP params set. Existing values
97
+ * will get preserved unless there is a new value for a parameter
98
+ * provided with params.
99
+ *
100
+ * Example Format:
101
+ *
102
+ * ```ts
103
+ * {
104
+ * 1559: {
105
+ * initialBaseFee: 1000000000,
106
+ * }
107
+ * }
108
+ * ```
109
+ *
110
+ * @param params
111
+ */
112
+ updateParams(params: ParamsDict) {
113
+ for (const [eip, paramsConfig] of Object.entries(params)) {
114
+ if (!(eip in this._params)) {
115
+ this._params[eip] = JSON.parse(JSON.stringify(paramsConfig)) // copy
116
+ } else {
117
+ this._params[eip] = JSON.parse(JSON.stringify({ ...this._params[eip], ...params[eip] })) // copy
118
+ }
119
+ }
120
+
121
+ this._buildParamsCache()
122
+ }
123
+
124
+ /**
125
+ * Fully resets the internal Common EIP params set with the values provided.
126
+ *
127
+ * Example Format:
128
+ *
129
+ * ```ts
130
+ * {
131
+ * 1559: {
132
+ * initialBaseFee: 1000000000,
133
+ * }
134
+ * }
135
+ * ```
136
+ *
137
+ * @param params
138
+ */
139
+ resetParams(params: ParamsDict) {
140
+ this._params = JSON.parse(JSON.stringify(params)) // copy
141
+ this._buildParamsCache()
142
+ }
143
+
144
+ /**
145
+ * Sets the hardfork to get params for
146
+ * @param hardfork String identifier (e.g. 'byzantium') or {@link Hardfork} enum
147
+ */
148
+ setHardfork(hardfork: string | Hardfork): void {
149
+ let existing = false
150
+ for (const hfChanges of this.HARDFORK_CHANGES) {
151
+ if (hfChanges[0] === hardfork) {
152
+ if (this._hardfork !== hardfork) {
153
+ this._hardfork = hardfork
154
+ this._buildParamsCache()
155
+ this._buildActivatedEIPsCache()
156
+ this.events.emit('hardforkChanged', hardfork)
157
+ }
158
+ existing = true
159
+ }
160
+ }
161
+ if (!existing) {
162
+ throw EthereumJSErrorWithoutCode(`Hardfork with name ${hardfork} not supported`)
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Returns the hardfork either based on block number (older HFs) or
168
+ * timestamp (Shanghai upwards).
169
+ *
170
+ * @param opts Block number or timestamp
171
+ * @returns The name of the HF
172
+ */
173
+ getHardforkBy(opts: HardforkByOpts): string {
174
+ const blockNumber: bigint | undefined = toType(opts.blockNumber, TypeOutput.BigInt)
175
+ const timestamp: bigint | undefined = toType(opts.timestamp, TypeOutput.BigInt)
176
+
177
+ // Filter out hardforks with no block number, no timestamp (i.e. unapplied hardforks)
178
+ const hfs = this.hardforks().filter((hf) => hf.block !== null || hf.timestamp !== undefined)
179
+
180
+ // Find the first hardfork that has a block number greater than `blockNumber`
181
+ // If timestamp is not provided, it also skips timestamps hardforks to continue
182
+ // discovering/checking number hardforks.
183
+ let hfIndex = hfs.findIndex(
184
+ (hf) =>
185
+ (blockNumber !== undefined && hf.block !== null && BigInt(hf.block) > blockNumber) ||
186
+ (timestamp !== undefined && hf.timestamp !== undefined && BigInt(hf.timestamp) > timestamp),
187
+ )
188
+
189
+ if (hfIndex === -1) {
190
+ // all hardforks apply, set hfIndex to the last one as that's the candidate
191
+ hfIndex = hfs.length
192
+ } else if (hfIndex === 0) {
193
+ // cannot have a case where a block number is before all applied hardforks
194
+ // since the chain has to start with a hardfork
195
+ throw Error('Must have at least one hardfork at block 0')
196
+ }
197
+
198
+ // If timestamp is not provided, we need to rollback to the last hf with block
199
+ if (timestamp === undefined) {
200
+ const stepBack = hfs
201
+ .slice(0, hfIndex)
202
+ .reverse()
203
+ .findIndex((hf) => hf.block !== null)
204
+ hfIndex = hfIndex - stepBack
205
+ }
206
+ // Move hfIndex one back to arrive at candidate hardfork
207
+ hfIndex = hfIndex - 1
208
+
209
+ const hfStartIndex = hfIndex
210
+ // Move the hfIndex to the end of the hardforks that might be scheduled on the same block/timestamp
211
+ // This won't anyway be the case with Merge hfs
212
+ for (; hfIndex < hfs.length - 1; hfIndex++) {
213
+ // break out if hfIndex + 1 is not scheduled at hfIndex
214
+ if (
215
+ hfs[hfIndex].block !== hfs[hfIndex + 1].block ||
216
+ hfs[hfIndex].timestamp !== hfs[hfIndex + 1].timestamp
217
+ ) {
218
+ break
219
+ }
220
+ }
221
+
222
+ if (timestamp !== undefined) {
223
+ const minTimeStamp = hfs
224
+ .slice(0, hfStartIndex)
225
+ .reduce(
226
+ (acc: number, hf: HardforkTransitionConfig) => Math.max(Number(hf.timestamp ?? '0'), acc),
227
+ 0,
228
+ )
229
+ if (minTimeStamp > timestamp) {
230
+ throw Error(`Maximum HF determined by timestamp is lower than the block number HF`)
231
+ }
232
+
233
+ const maxTimeStamp = hfs
234
+ .slice(hfIndex + 1)
235
+ .reduce(
236
+ (acc: number, hf: HardforkTransitionConfig) =>
237
+ Math.min(Number(hf.timestamp ?? timestamp), acc),
238
+ Number(timestamp),
239
+ )
240
+
241
+ if (maxTimeStamp < timestamp) {
242
+ throw Error(`Maximum HF determined by block number is lower than timestamp HF`)
243
+ }
244
+ }
245
+ const hardfork = hfs[hfIndex]
246
+ return hardfork.name
247
+ }
248
+
249
+ /**
250
+ * Sets a new hardfork either based on block number (older HFs) or
251
+ * timestamp (Shanghai upwards).
252
+ *
253
+ * @param opts Block number or timestamp
254
+ * @returns The name of the HF set
255
+ */
256
+ setHardforkBy(opts: HardforkByOpts): string {
257
+ const hardfork = this.getHardforkBy(opts)
258
+ this.setHardfork(hardfork)
259
+ return hardfork
260
+ }
261
+
262
+ /**
263
+ * Internal helper function, returns the params for the given hardfork for the chain set
264
+ * @param hardfork Hardfork name
265
+ * @returns Dictionary with hardfork params or null if hardfork not on chain
266
+ */
267
+ protected _getHardfork(hardfork: string | Hardfork): HardforkTransitionConfig | null {
268
+ const hfs = this.hardforks()
269
+ for (const hf of hfs) {
270
+ if (hf['name'] === hardfork) return hf
271
+ }
272
+ return null
273
+ }
274
+
275
+ /**
276
+ * Sets the active EIPs
277
+ * @param eips
278
+ */
279
+ setEIPs(eips: number[] = []) {
280
+ for (const eip of eips) {
281
+ if (!(eip in eipsDict)) {
282
+ throw EthereumJSErrorWithoutCode(`${eip} not supported`)
283
+ }
284
+ const minHF = this.gteHardfork(eipsDict[eip]['minimumHardfork'])
285
+ if (!minHF) {
286
+ throw EthereumJSErrorWithoutCode(
287
+ `${eip} cannot be activated on hardfork ${this.hardfork()}, minimumHardfork: ${minHF}`,
288
+ )
289
+ }
290
+ }
291
+ this._eips = eips
292
+ this._buildParamsCache()
293
+ this._buildActivatedEIPsCache()
294
+
295
+ for (const eip of eips) {
296
+ if (eipsDict[eip].requiredEIPs !== undefined) {
297
+ for (const elem of eipsDict[eip].requiredEIPs!) {
298
+ if (!(eips.includes(elem) || this.isActivatedEIP(elem))) {
299
+ throw EthereumJSErrorWithoutCode(
300
+ `${eip} requires EIP ${elem}, but is not included in the EIP list`,
301
+ )
302
+ }
303
+ }
304
+ }
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Internal helper for _buildParamsCache()
310
+ */
311
+ protected _mergeWithParamsCache(params: ParamsConfig) {
312
+ for (const [key, value] of Object.entries(params)) {
313
+ this._paramsCache[key] = value
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Build up a cache for all parameter values for the current HF and all activated EIPs
319
+ */
320
+ protected _buildParamsCache() {
321
+ this._paramsCache = {}
322
+
323
+ // Iterate through all hardforks up to hardfork set
324
+ const hardfork = this.hardfork()
325
+ for (const hfChanges of this.HARDFORK_CHANGES) {
326
+ // EIP-referencing HF config (e.g. for berlin)
327
+ if ('eips' in hfChanges[1]) {
328
+ const hfEIPs = hfChanges[1].eips ?? []
329
+ for (const eip of hfEIPs) {
330
+ if (this._params[eip] !== undefined && this._params[eip] !== null) {
331
+ this._mergeWithParamsCache(this._params[eip])
332
+ }
333
+ }
334
+ }
335
+ // Hardfork-scoped params (e.g. for bpo1, bpo2)
336
+ // override the baseline EIP values when present
337
+ const hfScopedParams = this._params[hfChanges[0]]
338
+ if (hfScopedParams !== undefined && hfScopedParams !== null) {
339
+ this._mergeWithParamsCache(hfScopedParams)
340
+ }
341
+ // Parameter-inlining HF config (e.g. for istanbul or custom blobSchedule)
342
+ if (hfChanges[1].params !== undefined && hfChanges[1].params !== null) {
343
+ this._mergeWithParamsCache(hfChanges[1].params)
344
+ }
345
+ if (hfChanges[0] === hardfork) break
346
+ }
347
+
348
+ // Iterate through all additionally activated EIPs
349
+ for (const eip of this._eips) {
350
+ if (this._params[eip] !== undefined && this._params[eip] !== null) {
351
+ this._mergeWithParamsCache(this._params[eip])
352
+ }
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Builds the cache of EIPs activated either via hardforks or constructor `eips`.
358
+ */
359
+ protected _buildActivatedEIPsCache() {
360
+ this._activatedEIPsCache = []
361
+
362
+ for (const [name, hf] of this.HARDFORK_CHANGES) {
363
+ if (this.gteHardfork(name) && 'eips' in hf) {
364
+ this._activatedEIPsCache = this._activatedEIPsCache.concat(hf.eips ?? [])
365
+ }
366
+ }
367
+ this._activatedEIPsCache = this._activatedEIPsCache.concat(this._eips)
368
+ }
369
+
370
+ /**
371
+ * Returns a parameter for the current chain setup
372
+ *
373
+ * If the parameter is present in an EIP, the EIP always takes precedence.
374
+ * Otherwise the parameter is taken from the latest applied HF with
375
+ * a change on the respective parameter.
376
+ *
377
+ * @param name Parameter name (e.g. 'minGasLimit')
378
+ * @returns The value requested (throws if not found)
379
+ */
380
+ param(name: string): bigint {
381
+ // TODO: consider the case that different active EIPs
382
+ // can change the same parameter
383
+ if (!(name in this._paramsCache)) {
384
+ throw EthereumJSErrorWithoutCode(`Missing parameter value for ${name}`)
385
+ }
386
+ const value = this._paramsCache[name]
387
+ return BigInt(value ?? 0)
388
+ }
389
+
390
+ /**
391
+ * Returns the parameter corresponding to a hardfork
392
+ * @param name Parameter name (e.g. 'minGasLimit')
393
+ * @param hardfork Hardfork name
394
+ * @returns The value requested (throws if not found)
395
+ */
396
+ paramByHardfork(name: string, hardfork: string | Hardfork): bigint {
397
+ let value
398
+ for (const hfChanges of this.HARDFORK_CHANGES) {
399
+ // EIP-referencing HF config (e.g. for berlin)
400
+ if ('eips' in hfChanges[1]) {
401
+ const hfEIPs = hfChanges[1]['eips']
402
+ for (const eip of hfEIPs!) {
403
+ const eipParams = this._params[eip]
404
+ const eipValue = eipParams?.[name]
405
+ if (eipValue !== undefined) {
406
+ value = eipValue
407
+ }
408
+ }
409
+ // Parameter-inlining HF config (e.g. for istanbul)
410
+ } else {
411
+ const hfValue = hfChanges[1].params?.[name]
412
+ if (hfValue !== undefined) {
413
+ value = hfValue
414
+ }
415
+ }
416
+ if (hfChanges[0] === hardfork) break
417
+ }
418
+ if (value === undefined) {
419
+ throw EthereumJSErrorWithoutCode(`Missing parameter value for ${name}`)
420
+ }
421
+ return BigInt(value ?? 0)
422
+ }
423
+
424
+ /**
425
+ * Returns a parameter corresponding to an EIP
426
+ * @param name Parameter name (e.g. 'minGasLimit' for 'gasConfig' topic)
427
+ * @param eip Number of the EIP
428
+ * @returns The value requested (throws if not found)
429
+ */
430
+ paramByEIP(name: string, eip: number): bigint | undefined {
431
+ if (!(eip in eipsDict)) {
432
+ throw EthereumJSErrorWithoutCode(`${eip} not supported`)
433
+ }
434
+
435
+ const eipParams = this._params[eip]
436
+ if (eipParams?.[name] === undefined) {
437
+ throw EthereumJSErrorWithoutCode(`Missing parameter value for ${name}`)
438
+ }
439
+ const value = eipParams![name]
440
+ return BigInt(value ?? 0)
441
+ }
442
+
443
+ /**
444
+ * Returns a parameter for the hardfork active on block number or
445
+ * optional provided total difficulty (Merge HF)
446
+ * @param name Parameter name
447
+ * @param blockNumber Block number
448
+ * @returns The value requested or `BigInt(0)` if not found
449
+ */
450
+ paramByBlock(name: string, blockNumber: BigIntLike, timestamp?: BigIntLike): bigint {
451
+ const hardfork = this.getHardforkBy({ blockNumber, timestamp })
452
+ return this.paramByHardfork(name, hardfork)
453
+ }
454
+
455
+ /**
456
+ * Returns the blob gas schedule for the current hardfork
457
+ * @returns The blob gas schedule
458
+ */
459
+ getBlobGasSchedule(): BpoSchedule {
460
+ if (this.gteHardfork(Hardfork.Bpo1)) {
461
+ return {
462
+ targetBlobGasPerBlock: this.param('target') * this.param('blobGasPerBlob'),
463
+ maxBlobGasPerBlock: this.param('max') * this.param('blobGasPerBlob'),
464
+ blobGasPriceUpdateFraction: this.param('blobGasPriceUpdateFraction'),
465
+ }
466
+ }
467
+ return {
468
+ targetBlobGasPerBlock: this.param('targetBlobGasPerBlock'),
469
+ maxBlobGasPerBlock: this.param('maxBlobGasPerBlock'),
470
+ blobGasPriceUpdateFraction: this.param('blobGasPriceUpdateFraction'),
471
+ }
472
+ }
473
+
474
+ /**
475
+ * Checks if an EIP is activated by either being included in the EIPs
476
+ * manually passed in with the {@link CommonOpts.eips} or in a
477
+ * hardfork currently being active
478
+ *
479
+ * Note: this method only works for EIPs being supported
480
+ * by the {@link CommonOpts.eips} constructor option
481
+ * @param eip
482
+ */
483
+ isActivatedEIP(eip: number): boolean {
484
+ if (this._activatedEIPsCache.includes(eip)) {
485
+ return true
486
+ }
487
+ return false
488
+ }
489
+
490
+ /**
491
+ * Checks if set or provided hardfork is active on block number
492
+ * @param hardfork Hardfork name or null (for HF set)
493
+ * @param blockNumber
494
+ * @returns True if HF is active on block number
495
+ */
496
+ hardforkIsActiveOnBlock(hardfork: string | Hardfork | null, blockNumber: BigIntLike): boolean {
497
+ blockNumber = toType(blockNumber, TypeOutput.BigInt)
498
+ hardfork = hardfork ?? this._hardfork
499
+ const hfBlock = this.hardforkBlock(hardfork)
500
+ if (typeof hfBlock === 'bigint' && hfBlock !== BIGINT_0 && blockNumber >= hfBlock) {
501
+ return true
502
+ }
503
+ return false
504
+ }
505
+
506
+ /**
507
+ * Alias to hardforkIsActiveOnBlock when hardfork is set
508
+ * @param blockNumber
509
+ * @returns True if HF is active on block number
510
+ */
511
+ activeOnBlock(blockNumber: BigIntLike): boolean {
512
+ return this.hardforkIsActiveOnBlock(null, blockNumber)
513
+ }
514
+
515
+ /**
516
+ * Sequence based check if given or set HF1 is greater than or equal HF2
517
+ * @param hardfork1 Hardfork name or null (if set)
518
+ * @param hardfork2 Hardfork name
519
+ * @returns True if HF1 gte HF2
520
+ */
521
+ hardforkGteHardfork(hardfork1: string | Hardfork | null, hardfork2: string | Hardfork): boolean {
522
+ hardfork1 = hardfork1 ?? this._hardfork
523
+ const hardforks = this.hardforks()
524
+
525
+ let posHf1 = -1,
526
+ posHf2 = -1
527
+ let index = 0
528
+ for (const hf of hardforks) {
529
+ if (hf['name'] === hardfork1) posHf1 = index
530
+ if (hf['name'] === hardfork2) posHf2 = index
531
+ index += 1
532
+ }
533
+ return posHf1 >= posHf2 && posHf2 !== -1
534
+ }
535
+
536
+ /**
537
+ * Alias to hardforkGteHardfork when hardfork is set
538
+ * @param hardfork Hardfork name
539
+ * @returns True if hardfork set is greater than hardfork provided
540
+ */
541
+ gteHardfork(hardfork: string | Hardfork): boolean {
542
+ return this.hardforkGteHardfork(null, hardfork)
543
+ }
544
+
545
+ /**
546
+ * Returns the hardfork change block for hardfork provided or set
547
+ * @param hardfork Hardfork name, optional if HF set
548
+ * @returns Block number or null if unscheduled
549
+ */
550
+ hardforkBlock(hardfork?: string | Hardfork): bigint | null {
551
+ hardfork = hardfork ?? this._hardfork
552
+ const block = this._getHardfork(hardfork)?.['block']
553
+ if (block === undefined || block === null) {
554
+ return null
555
+ }
556
+ return BigInt(block)
557
+ }
558
+
559
+ /**
560
+ * Returns the timestamp at which a given hardfork is scheduled (if any).
561
+ * @param hardfork Hardfork name, optional if HF set
562
+ * @returns Timestamp or null if the hardfork is not timestamp-based
563
+ */
564
+ hardforkTimestamp(hardfork?: string | Hardfork): bigint | null {
565
+ hardfork = hardfork ?? this._hardfork
566
+ const timestamp = this._getHardfork(hardfork)?.['timestamp']
567
+ if (timestamp === undefined || timestamp === null) {
568
+ return null
569
+ }
570
+ return BigInt(timestamp)
571
+ }
572
+
573
+ /**
574
+ * Returns the hardfork change block for eip
575
+ * @param eip EIP number
576
+ * @returns Block number or null if unscheduled
577
+ */
578
+ eipBlock(eip: number): bigint | null {
579
+ for (const hfChanges of this.HARDFORK_CHANGES) {
580
+ const hf = hfChanges[1]
581
+ if ('eips' in hf) {
582
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
583
+ if ((hf['eips'] as any).includes(eip)) {
584
+ return this.hardforkBlock(hfChanges[0])
585
+ }
586
+ }
587
+ }
588
+ return null
589
+ }
590
+
591
+ /**
592
+ * Returns the scheduled timestamp of the EIP (if scheduled and scheduled by timestamp)
593
+ * @param eip EIP number
594
+ * @returns Scheduled timestamp. If this EIP is unscheduled, or the EIP is scheduled by block number, then it returns `null`.
595
+ */
596
+ eipTimestamp(eip: number): bigint | null {
597
+ for (const hfChanges of this.HARDFORK_CHANGES) {
598
+ const hf = hfChanges[1]
599
+ if ('eips' in hf) {
600
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
601
+ if ((hf['eips'] as any).includes(eip)) {
602
+ return this.hardforkTimestamp(hfChanges[0])
603
+ }
604
+ }
605
+ }
606
+ return null
607
+ }
608
+
609
+ /**
610
+ * Returns the block number or timestamp at which the next hardfork will occur.
611
+ * For pre-merge hardforks, returns the block number.
612
+ * For post-merge hardforks, returns the timestamp.
613
+ * Returns null if there is no next hardfork.
614
+ * @param hardfork Hardfork name, optional if HF set
615
+ * @returns Block number or timestamp, or null if not available
616
+ */
617
+ nextHardforkBlockOrTimestamp(hardfork?: string | Hardfork): bigint | null {
618
+ const targetHardfork = hardfork ?? this._hardfork
619
+ const hfs = this.hardforks()
620
+
621
+ // Find the index of the target hardfork
622
+ let targetHfIndex = hfs.findIndex((hf) => hf.name === targetHardfork)
623
+
624
+ // Special handling for The Merge (Paris) hardfork
625
+ if (targetHardfork === Hardfork.Paris) {
626
+ // The Merge is determined by total difficulty, not block number
627
+ // So we look at the previous hardfork's parameters instead
628
+ targetHfIndex -= 1
629
+ }
630
+
631
+ // If we couldn't find a valid hardfork index, return null
632
+ if (targetHfIndex < 0) {
633
+ return null
634
+ }
635
+
636
+ // Get the current hardfork's block/timestamp
637
+ const currentHf = hfs[targetHfIndex]
638
+ const currentBlockOrTimestamp = currentHf.timestamp ?? currentHf.block
639
+ if (currentBlockOrTimestamp === null || currentBlockOrTimestamp === undefined) {
640
+ return null
641
+ }
642
+
643
+ // Find the next hardfork that has a different block/timestamp
644
+ const nextHf = hfs.slice(targetHfIndex + 1).find((hf) => {
645
+ const nextBlockOrTimestamp = hf.timestamp ?? hf.block
646
+ return (
647
+ nextBlockOrTimestamp !== null &&
648
+ nextBlockOrTimestamp !== undefined &&
649
+ nextBlockOrTimestamp !== currentBlockOrTimestamp
650
+ )
651
+ })
652
+ // If no next hf found with valid block or timestamp return null
653
+ if (nextHf === undefined) {
654
+ return null
655
+ }
656
+
657
+ // Get the block/timestamp for the next hardfork
658
+ const nextBlockOrTimestamp = nextHf.timestamp ?? nextHf.block
659
+ if (nextBlockOrTimestamp === null || nextBlockOrTimestamp === undefined) {
660
+ return null
661
+ }
662
+
663
+ return BigInt(nextBlockOrTimestamp)
664
+ }
665
+
666
+ /**
667
+ * Internal helper function to calculate a fork hash
668
+ * @param hardfork Hardfork name
669
+ * @param genesisHash Genesis block hash of the chain
670
+ * @returns Fork hash as hex string
671
+ */
672
+ protected _calcForkHash(hardfork: string | Hardfork, genesisHash: Uint8Array): PrefixedHexString {
673
+ let hfBytes = new Uint8Array(0)
674
+ let prevBlockOrTime = 0
675
+ for (const hf of this.hardforks()) {
676
+ const { block, timestamp, name } = hf
677
+ // Timestamp to be used for timestamp based hfs even if we may bundle
678
+ // block number with them retrospectively
679
+ let blockOrTime = timestamp ?? block
680
+ blockOrTime = blockOrTime !== null ? Number(blockOrTime) : null
681
+
682
+ // Skip for chainstart (0), not applied HFs (null) and
683
+ // when already applied on same blockOrTime HFs
684
+ // and on the merge since forkhash doesn't change on merge hf
685
+ if (
686
+ typeof blockOrTime === 'number' &&
687
+ blockOrTime !== 0 &&
688
+ blockOrTime !== prevBlockOrTime &&
689
+ name !== Hardfork.Paris
690
+ ) {
691
+ const hfBlockBytes = hexToBytes(`0x${blockOrTime.toString(16).padStart(16, '0')}`)
692
+ hfBytes = concatBytes(hfBytes, hfBlockBytes)
693
+ prevBlockOrTime = blockOrTime
694
+ }
695
+
696
+ if (hf.name === hardfork) break
697
+ }
698
+ const inputBytes = concatBytes(genesisHash, hfBytes)
699
+
700
+ // CRC32 delivers result as signed (negative) 32-bit integer,
701
+ // convert to hex string
702
+ const forkhash = bytesToHex(intToBytes(crc32(inputBytes) >>> 0))
703
+ return forkhash
704
+ }
705
+
706
+ /**
707
+ * Returns an eth/64 compliant fork hash (EIP-2124)
708
+ * @param hardfork Hardfork name, optional if HF set
709
+ * @param genesisHash Genesis block hash of the network, optional if already defined and not needed to be calculated
710
+ * @returns Fork hash as a hex string
711
+ */
712
+ forkHash(hardfork?: string | Hardfork, genesisHash?: Uint8Array): PrefixedHexString {
713
+ hardfork = hardfork ?? this._hardfork
714
+ const data = this._getHardfork(hardfork)
715
+ if (data === null || (data?.block === null && data?.timestamp === undefined)) {
716
+ const msg = 'No fork hash calculation possible for future hardfork'
717
+ throw EthereumJSErrorWithoutCode(msg)
718
+ }
719
+ if (data?.forkHash !== null && data?.forkHash !== undefined) {
720
+ return data.forkHash
721
+ }
722
+ if (!genesisHash)
723
+ throw EthereumJSErrorWithoutCode('genesisHash required for forkHash calculation')
724
+ return this._calcForkHash(hardfork, genesisHash)
725
+ }
726
+
727
+ /**
728
+ *
729
+ * @param forkHash Fork hash as a hex string
730
+ * @returns Array with hardfork data (name, block, forkHash)
731
+ */
732
+ hardforkForForkHash(forkHash: string): HardforkTransitionConfig | null {
733
+ const resArray = this.hardforks().filter((hf: HardforkTransitionConfig) => {
734
+ return hf.forkHash === forkHash
735
+ })
736
+ return resArray.length >= 1 ? resArray[resArray.length - 1] : null
737
+ }
738
+
739
+ /**
740
+ * Sets any missing forkHashes on this {@link Common} instance.
741
+ * @param genesisHash The genesis block hash
742
+ */
743
+ setForkHashes(genesisHash: Uint8Array) {
744
+ for (const hf of this.hardforks()) {
745
+ const blockOrTime = hf.timestamp ?? hf.block
746
+ if (
747
+ (hf.forkHash === null || hf.forkHash === undefined) &&
748
+ blockOrTime !== null &&
749
+ blockOrTime !== undefined
750
+ ) {
751
+ hf.forkHash = this.forkHash(hf.name, genesisHash)
752
+ }
753
+ }
754
+ }
755
+
756
+ /**
757
+ * Returns the Genesis parameters of the current chain
758
+ * @returns Genesis dictionary
759
+ */
760
+ genesis(): GenesisBlockConfig {
761
+ return this._chainParams.genesis
762
+ }
763
+
764
+ /**
765
+ * Returns the hardfork definitions for the current chain.
766
+ * @returns Array of hardfork transition configs
767
+ */
768
+ hardforks(): HardforkTransitionConfig[] {
769
+ const hfs = this._chainParams.hardforks
770
+ if (this._chainParams.customHardforks !== undefined) {
771
+ // Add transition configs for custom hardforks that aren't already in the hardforks array
772
+ const existingNames = new Set(hfs.map((hf) => hf.name))
773
+ const customHfEntries = Object.keys(this._chainParams.customHardforks)
774
+ .filter((name) => !existingNames.has(name))
775
+ .map((name) => ({
776
+ name,
777
+ block: null, // Custom hardforks without explicit transition config default to null (inactive by block)
778
+ }))
779
+ return [...hfs, ...customHfEntries]
780
+ }
781
+ return hfs
782
+ }
783
+
784
+ /**
785
+ * Returns bootstrap nodes for the current chain.
786
+ * @returns Array of bootstrap node configs
787
+ */
788
+ bootstrapNodes(): BootstrapNodeConfig[] {
789
+ return this._chainParams.bootstrapNodes
790
+ }
791
+
792
+ /**
793
+ * Returns DNS networks for the current chain
794
+ * @returns {String[]} Array of DNS ENR urls
795
+ */
796
+ dnsNetworks(): string[] {
797
+ return this._chainParams.dnsNetworks!
798
+ }
799
+
800
+ /**
801
+ * Returns the hardfork set
802
+ * @returns Hardfork name
803
+ */
804
+ hardfork(): string | Hardfork {
805
+ return this._hardfork
806
+ }
807
+
808
+ /**
809
+ * Returns the Id of current chain
810
+ * @returns chain Id
811
+ */
812
+ chainId(): bigint {
813
+ return BigInt(this._chainParams.chainId)
814
+ }
815
+
816
+ /**
817
+ * Returns the name of current chain
818
+ * @returns chain name (lower case)
819
+ */
820
+ chainName(): string {
821
+ return this._chainParams.name
822
+ }
823
+
824
+ /**
825
+ * Returns the additionally activated EIPs
826
+ * (by using the `eips` constructor option)
827
+ * @returns List of EIPs
828
+ */
829
+ eips(): number[] {
830
+ return this._eips
831
+ }
832
+
833
+ /**
834
+ * Returns the consensus type of the network
835
+ * Possible values: "pow"|"poa"|"pos"
836
+ *
837
+ * Note: This value can update along a Hardfork.
838
+ */
839
+ consensusType(): string | ConsensusType {
840
+ const hardfork = this.hardfork()
841
+
842
+ let value
843
+ for (const hfChanges of this.HARDFORK_CHANGES) {
844
+ if ('consensus' in hfChanges[1]) {
845
+ value = (hfChanges[1] as any)['consensus']['type']
846
+ }
847
+ if (hfChanges[0] === hardfork) break
848
+ }
849
+ return value ?? this._chainParams['consensus']['type']
850
+ }
851
+
852
+ /**
853
+ * Returns the concrete consensus implementation
854
+ * algorithm or protocol for the network
855
+ * e.g. "ethash" for "pow" consensus type,
856
+ * "clique" for "poa" consensus type or
857
+ * "casper" for "pos" consensus type.
858
+ *
859
+ * Note: This value can update along a Hardfork.
860
+ */
861
+ consensusAlgorithm(): string | ConsensusAlgorithm {
862
+ const hardfork = this.hardfork()
863
+
864
+ let value
865
+ for (const hfChanges of this.HARDFORK_CHANGES) {
866
+ if ('consensus' in hfChanges[1]) {
867
+ value = hfChanges[1]['consensus']!['algorithm']
868
+ }
869
+ if (hfChanges[0] === hardfork) break
870
+ }
871
+ return value ?? (this._chainParams['consensus']['algorithm'] as ConsensusAlgorithm)
872
+ }
873
+
874
+ /**
875
+ * Returns a dictionary with consensus configuration
876
+ * parameters based on the consensus algorithm
877
+ *
878
+ * Expected returns (parameters must be present in
879
+ * the respective chain JSON files):
880
+ *
881
+ * ethash: empty object
882
+ * clique: period, epoch
883
+ * casper: empty object
884
+ *
885
+ * Note: This value can update along a Hardfork.
886
+ */
887
+ consensusConfig(): { [key: string]: CliqueConfig | EthashConfig | CasperConfig } {
888
+ const hardfork = this.hardfork()
889
+
890
+ let value
891
+ for (const hfChanges of this.HARDFORK_CHANGES) {
892
+ if ('consensus' in hfChanges[1]) {
893
+ // The config parameter is named after the respective consensus algorithm
894
+ const config = hfChanges[1]
895
+ const algorithm = config['consensus']!['algorithm']
896
+ value = (config['consensus'] as any)[algorithm]
897
+ }
898
+ if (hfChanges[0] === hardfork) break
899
+ }
900
+ return (
901
+ value ?? this._chainParams['consensus'][this.consensusAlgorithm() as ConsensusAlgorithm] ?? {}
902
+ )
903
+ }
904
+
905
+ /**
906
+ * Returns a deep copy of this {@link Common} instance.
907
+ */
908
+ copy(): Common {
909
+ const copy = Object.assign(Object.create(Object.getPrototypeOf(this)), this)
910
+ copy.events = new EventEmitter()
911
+ return copy
912
+ }
913
+ }