rocketh 0.14.8 → 0.15.0-testing.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.
@@ -1,6 +1,8 @@
1
1
  import type {Chain} from 'viem/chains';
2
2
  import * as chains from 'viem/chains';
3
3
  import {ResolvedConfig} from '../types.js';
4
+ import {kebabCase} from 'change-case';
5
+ import {ChainInfo} from '../../executor/types.js';
4
6
 
5
7
  export type ChainType = 'zksync' | 'op-stack' | 'celo' | 'default';
6
8
 
@@ -27,10 +29,10 @@ const chainTypesByNames: {[chainExportName: string]: ChainType} = {
27
29
 
28
30
  export const chainTypes: {[chainId: string]: ChainType} = {};
29
31
 
30
- export const chainById: {[chainId: string]: Chain} = {};
31
- const allChains = (chains as any).default || chains;
32
+ export const chainById: {[chainId: string]: ChainInfo} = {};
33
+ export const allChains = (chains as any).default || chains;
32
34
  for (const key of Object.keys(allChains)) {
33
- const chain = (allChains as any)[key] as Chain;
35
+ const chain = (allChains as any)[key] as ChainInfo;
34
36
  const chainId = chain.id.toString();
35
37
  const specificChainType = chainTypesByNames[key];
36
38
  if (specificChainType) {
@@ -39,28 +41,37 @@ for (const key of Object.keys(allChains)) {
39
41
  chainById[chainId] = chain;
40
42
  }
41
43
 
42
- export function getChain(id: string): Chain | undefined {
43
- const chain = chainById[id];
44
+ export const chainByCanonicalName: {[canonicalName: string]: ChainInfo} = {};
45
+ for (const key of Object.keys(allChains)) {
46
+ const chain = (allChains as any)[key] as ChainInfo;
47
+ const canonicalName = kebabCase(chain.name);
48
+ chainByCanonicalName[canonicalName] = chain;
49
+ }
50
+
51
+ export function getChain(id: string | number): ChainInfo | undefined {
52
+ const chain = chainById['' + id];
53
+
54
+ return chain;
55
+ }
44
56
 
57
+ export function getChainByName(name: string): ChainInfo | undefined {
58
+ const chain = chainByCanonicalName[name];
45
59
  return chain;
46
60
  }
47
61
 
48
- export function getChainWithConfig(id: string, config: ResolvedConfig): Chain {
62
+ export function getChainWithConfig(id: string, config: ResolvedConfig): ChainInfo {
49
63
  const chain = getChain(id);
50
64
 
51
65
  if (!chain) {
52
- if (config.network.publicInfo) {
53
- return {
54
- id: parseInt(id),
55
- ...config.network.publicInfo,
56
- };
66
+ if (config.target.chain) {
67
+ return config.target.chain;
57
68
  }
58
- console.error(`network ${config.network.name} has no public info`);
69
+ console.error(`network ${config.target.name} has no public info`);
59
70
  let nodeUrl: string | undefined;
60
- if (!config.network.nodeUrl) {
61
- console.error(`no nodeUrl found either for ${config.network.name}`);
71
+ if (!config.target.nodeUrl) {
72
+ console.error(`no nodeUrl found either for ${config.target.name}`);
62
73
  } else {
63
- nodeUrl = config.network.nodeUrl;
74
+ nodeUrl = config.target.nodeUrl;
64
75
  }
65
76
  return {
66
77
  id: parseInt(id),
@@ -75,6 +86,7 @@ export function getChainWithConfig(id: string, config: ResolvedConfig): Chain {
75
86
  http: nodeUrl ? [nodeUrl] : [],
76
87
  },
77
88
  },
89
+ chainType: 'default',
78
90
  };
79
91
  }
80
92
  return chain;
@@ -7,17 +7,27 @@ import {formatEther} from 'viem';
7
7
  import type {
8
8
  Config,
9
9
  Environment,
10
+ JSONTypePlusBigInt,
10
11
  ResolvedConfig,
12
+ TargetConfig,
11
13
  UnknownDeployments,
12
14
  UnresolvedNetworkSpecificData,
13
15
  UnresolvedUnknownNamedAccounts,
14
16
  } from '../environment/types.js';
15
17
  import {createEnvironment, SignerProtocolFunction} from '../environment/index.js';
16
- import {DeployScriptFunction, DeployScriptModule, EnhancedDeployScriptFunction, EnhancedEnvironment} from './types.js';
18
+ import {
19
+ ChainInfo,
20
+ DeployScriptFunction,
21
+ DeployScriptModule,
22
+ EnhancedDeployScriptFunction,
23
+ EnhancedEnvironment,
24
+ } from './types.js';
17
25
  import {withEnvironment} from '../utils/extensions.js';
18
26
  import {logger, setLogLevel, spin} from '../internal/logging.js';
19
27
  import {getRoughGasPriceEstimate} from '../utils/eth.js';
20
28
  import {traverseMultipleDirectory} from '../utils/fs.js';
29
+ import {getChain, getChainByName} from '../environment/utils/chains.js';
30
+ import {JSONRPCHTTPProvider} from 'eip-1193-jsonrpc-provider';
21
31
 
22
32
  // @ts-ignore
23
33
  const tsImport = (path: string, opts: any) => (typeof Bun !== 'undefined' ? import(path) : tsImport_(path, opts));
@@ -146,7 +156,7 @@ export type UntypedEIP1193Provider = {
146
156
  };
147
157
 
148
158
  export type ConfigOptions<Extra extends Record<string, unknown> = Record<string, unknown>> = {
149
- network?: string | {fork: string};
159
+ target?: string | {fork: string};
150
160
  deployments?: string;
151
161
  scripts?: string | string[];
152
162
  tags?: string;
@@ -180,34 +190,30 @@ export type DeterministicDeploymentInfo =
180
190
  create3?: Create3DeterministicDeploymentInfo;
181
191
  };
182
192
 
183
- type Networks = {
184
- [name: string]: {
185
- rpcUrl?: string;
186
- tags?: string[];
187
- deterministicDeployment?: DeterministicDeploymentInfo;
188
- scripts?: string | string[];
189
- publicInfo?: {
190
- name: string;
191
- nativeCurrency: {
192
- name: string;
193
- symbol: string;
194
- decimals: number;
195
- };
196
- rpcUrls: {
197
- default: {
198
- http: string[];
199
- };
200
- };
201
- chainType?: string;
202
- };
203
- pollingInterval?: number;
204
- };
193
+ export type ChainUserConfig = {
194
+ rpcUrl?: string;
195
+ tags?: string[];
196
+ deterministicDeployment?: DeterministicDeploymentInfo;
197
+ info?: ChainInfo;
198
+ pollingInterval?: number;
199
+ properties?: Record<string, JSONTypePlusBigInt>;
200
+ };
201
+
202
+ export type DeploymentTargetConfig = {
203
+ chainId: number;
204
+ scripts?: string | string[];
205
+ overrides: Omit<ChainUserConfig, 'info'>;
206
+ };
207
+
208
+ export type Chains = {
209
+ [idOrName: number | string]: ChainUserConfig;
205
210
  };
206
211
  export type UserConfig<
207
212
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
208
213
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData
209
214
  > = {
210
- networks?: Networks;
215
+ targets: {[name: string]: DeploymentTargetConfig};
216
+ chains?: Chains;
211
217
  deployments?: string;
212
218
  scripts?: string | string[];
213
219
  accounts?: NamedAccounts;
@@ -216,11 +222,14 @@ export type UserConfig<
216
222
  defaultPollingInterval?: number;
217
223
  };
218
224
 
219
- export function transformUserConfig<
225
+ export async function transformUserConfig<
220
226
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
221
227
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
222
228
  Extra extends Record<string, unknown> = Record<string, unknown>
223
- >(configFile: UserConfig<NamedAccounts, Data> | undefined, options: ConfigOptions<Extra>): Config<NamedAccounts, Data> {
229
+ >(
230
+ configFile: UserConfig<NamedAccounts, Data> | undefined,
231
+ options: ConfigOptions<Extra>
232
+ ): Promise<Config<NamedAccounts, Data>> {
224
233
  if (configFile) {
225
234
  if (!options.deployments && configFile.deployments) {
226
235
  options.deployments = configFile.deployments;
@@ -230,118 +239,145 @@ export function transformUserConfig<
230
239
  }
231
240
  }
232
241
 
233
- const fromEnv = process.env['ETH_NODE_URI_' + options.network];
234
- const fork = typeof options.network !== 'string';
235
- let networkName = 'memory';
236
- if (options.network) {
237
- if (typeof options.network === 'string') {
238
- networkName = options.network;
239
- } else if ('fork' in options.network) {
240
- networkName = options.network.fork;
242
+ const fork = typeof options.target !== 'string';
243
+ let targetName = 'memory';
244
+ if (options.target) {
245
+ if (typeof options.target === 'string') {
246
+ targetName = options.target;
247
+ } else if ('fork' in options.target) {
248
+ targetName = options.target.fork;
249
+ }
250
+ }
251
+
252
+ let chainInfo: ChainInfo;
253
+
254
+ let chainId: number;
255
+
256
+ if (configFile?.targets[targetName]) {
257
+ chainId = configFile.targets[targetName].chainId;
258
+ } else {
259
+ const chainFound = getChainByName(targetName);
260
+ if (chainFound) {
261
+ chainInfo = chainFound;
262
+ chainId = chainInfo.id;
263
+ } else {
264
+ if (options.provider) {
265
+ const chainIdAsHex = await (options.provider as EIP1193ProviderWithoutEvents).request({method: 'eth_chainId'});
266
+ chainId = Number(chainIdAsHex);
267
+ } else {
268
+ throw new Error(`target ${targetName} is unknown`);
269
+ }
241
270
  }
242
271
  }
243
272
 
244
- let defaultTags: string[] = [];
273
+ const chainFound = getChain(chainId);
274
+ if (chainFound) {
275
+ chainInfo = chainFound;
276
+ } else {
277
+ throw new Error(`target ${targetName} has no chain associated with it`);
278
+ }
279
+
280
+ const chainConfigFromChainId = configFile?.chains?.[chainId];
281
+ const chainConfigFromChainName = configFile?.chains?.[targetName];
282
+ if (chainConfigFromChainId && chainConfigFromChainName) {
283
+ throw new Error(
284
+ `conflicting config for chain, choose to configure via its chainId (${chainId}) or via its name ${targetName} but not both`
285
+ );
286
+ }
287
+ const chainConfig = chainConfigFromChainId || chainConfigFromChainName;
288
+
289
+ const targetConfig = configFile?.targets?.[targetName];
290
+ const actualChainConfig = targetConfig?.overrides
291
+ ? {
292
+ ...chainConfig,
293
+ ...targetConfig.overrides,
294
+ properties: {
295
+ ...chainConfig?.properties,
296
+ ...targetConfig.overrides.properties,
297
+ },
298
+ }
299
+ : chainConfig;
300
+
301
+ const defaultTags: string[] = [];
302
+ if (chainInfo.testnet) {
303
+ defaultTags.push('testnet');
304
+ }
245
305
 
246
- let networkTags: string[] =
247
- (configFile?.networks && (configFile?.networks[networkName]?.tags || configFile?.networks['default']?.tags)) ||
248
- defaultTags;
306
+ let targetTags: string[] = actualChainConfig?.tags || defaultTags;
249
307
 
250
- let networkScripts: string | string[] | undefined =
251
- (configFile?.networks &&
252
- (configFile?.networks[networkName]?.scripts || configFile?.networks['default']?.scripts)) ||
253
- undefined;
308
+ let networkScripts: string | string[] | undefined = targetConfig?.scripts;
254
309
 
255
310
  // no default for publicInfo
256
- const publicInfo = configFile?.networks ? configFile?.networks[networkName]?.publicInfo : undefined;
257
311
  const defaultPollingInterval = configFile?.defaultPollingInterval;
258
- const pollingInterval = configFile?.networks?.[networkName]?.pollingInterval;
259
- const deterministicDeployment = configFile?.networks?.[networkName]?.deterministicDeployment;
312
+ const pollingInterval = actualChainConfig?.pollingInterval;
313
+ const deterministicDeployment = actualChainConfig?.deterministicDeployment;
314
+ const properties = actualChainConfig?.properties;
315
+
316
+ let resolvedTargetConfig: TargetConfig;
317
+
260
318
  if (!options.provider) {
319
+ let rpcURL = actualChainConfig?.rpcUrl;
320
+ if (!rpcURL) {
321
+ rpcURL = chainInfo.rpcUrls.default.http[0];
322
+ }
323
+
324
+ const fromEnv = process.env['ETH_NODE_URI_' + targetName];
261
325
  let nodeUrl: string;
262
- if (typeof fromEnv === 'string') {
326
+ if (typeof fromEnv === 'string' && fromEnv) {
263
327
  nodeUrl = fromEnv;
264
328
  } else {
265
- if (configFile) {
266
- const network = configFile.networks && configFile.networks[networkName];
267
- if (network && network.rpcUrl) {
268
- nodeUrl = network.rpcUrl;
269
- } else {
270
- if (options?.ignoreMissingRPC) {
271
- nodeUrl = '';
272
- } else {
273
- if (options.network === 'localhost') {
274
- nodeUrl = 'http://127.0.0.1:8545';
275
- } else {
276
- console.error(`network "${options.network}" is not configured. Please add it to the rocketh.js/ts file`);
277
- process.exit(1);
278
- }
279
- }
280
- }
329
+ if (rpcURL) {
330
+ nodeUrl = rpcURL;
331
+ } else if (options?.ignoreMissingRPC) {
332
+ nodeUrl = '';
333
+ } else if (options.target === 'localhost') {
334
+ nodeUrl = 'http://127.0.0.1:8545';
281
335
  } else {
282
- if (options?.ignoreMissingRPC) {
283
- nodeUrl = '';
284
- } else {
285
- if (options.network === 'localhost') {
286
- nodeUrl = 'http://127.0.0.1:8545';
287
- } else {
288
- console.error(`network "${options.network}" is not configured. Please add it to the rocketh.js/ts file`);
289
- process.exit(1);
290
- }
291
- }
336
+ console.error(`network "${options.target}" is not configured. Please add it to the rocketh.js/ts file`);
337
+ process.exit(1);
292
338
  }
293
339
  }
294
340
 
295
- return {
296
- network: {
297
- nodeUrl,
298
- name: networkName,
299
- tags: networkTags,
300
- fork,
301
- deterministicDeployment,
302
- scripts: networkScripts,
303
- publicInfo,
304
- pollingInterval,
305
- },
306
- deployments: options.deployments,
307
- saveDeployments: options.saveDeployments,
308
- scripts: options.scripts,
309
- data: configFile?.data,
310
- tags: typeof options.tags === 'undefined' ? undefined : options.tags.split(','),
311
- logLevel: options.logLevel,
312
- askBeforeProceeding: options.askBeforeProceeding,
313
- reportGasUse: options.reportGasUse,
314
- accounts: configFile?.accounts,
315
- signerProtocols: configFile?.signerProtocols,
316
- extra: options.extra,
317
- defaultPollingInterval,
341
+ resolvedTargetConfig = {
342
+ nodeUrl,
343
+ name: targetName,
344
+ tags: targetTags,
345
+ fork,
346
+ deterministicDeployment,
347
+ scripts: networkScripts,
348
+ chainInfo,
349
+ pollingInterval,
350
+ properties,
318
351
  };
319
352
  } else {
320
- return {
321
- network: {
322
- provider: options.provider as EIP1193ProviderWithoutEvents,
323
- name: networkName,
324
- tags: networkTags,
325
- fork,
326
- deterministicDeployment,
327
- scripts: networkScripts,
328
- publicInfo,
329
- pollingInterval,
330
- },
331
- deployments: options.deployments,
332
- saveDeployments: options.saveDeployments,
333
- scripts: options.scripts,
334
- data: configFile?.data,
335
- tags: typeof options.tags === 'undefined' ? undefined : options.tags.split(','),
336
- logLevel: options.logLevel,
337
- askBeforeProceeding: options.askBeforeProceeding,
338
- reportGasUse: options.reportGasUse,
339
- accounts: configFile?.accounts,
340
- signerProtocols: configFile?.signerProtocols,
341
- extra: options.extra,
342
- defaultPollingInterval,
353
+ resolvedTargetConfig = {
354
+ provider: options.provider as EIP1193ProviderWithoutEvents,
355
+ name: targetName,
356
+ tags: targetTags,
357
+ fork,
358
+ deterministicDeployment,
359
+ scripts: networkScripts,
360
+ chainInfo,
361
+ pollingInterval,
362
+ properties,
343
363
  };
344
364
  }
365
+
366
+ return {
367
+ target: resolvedTargetConfig,
368
+ deployments: options.deployments,
369
+ saveDeployments: options.saveDeployments,
370
+ scripts: options.scripts,
371
+ data: configFile?.data,
372
+ tags: typeof options.tags === 'undefined' ? undefined : options.tags.split(','),
373
+ logLevel: options.logLevel,
374
+ askBeforeProceeding: options.askBeforeProceeding,
375
+ reportGasUse: options.reportGasUse,
376
+ accounts: configFile?.accounts,
377
+ signerProtocols: configFile?.signerProtocols,
378
+ extra: options.extra,
379
+ defaultPollingInterval,
380
+ };
345
381
  }
346
382
 
347
383
  export async function readConfig<
@@ -418,26 +454,26 @@ export function resolveConfig<
418
454
  } as const;
419
455
 
420
456
  const defaultPollingInterval = config.defaultPollingInterval || 1;
421
- const networkPollingInterval = config.network.pollingInterval || defaultPollingInterval;
457
+ const networkPollingInterval = config.target.pollingInterval || defaultPollingInterval;
422
458
 
423
459
  let deterministicDeployment: {
424
460
  create2: Create2DeterministicDeploymentInfo;
425
461
  create3: Create3DeterministicDeploymentInfo;
426
462
  } = {
427
463
  create2: (() => {
428
- if (!config.network.deterministicDeployment) return create2Info;
464
+ if (!config.target.deterministicDeployment) return create2Info;
429
465
  if (
430
- !('create3' in config.network.deterministicDeployment) &&
431
- !('create2' in config.network.deterministicDeployment)
466
+ !('create3' in config.target.deterministicDeployment) &&
467
+ !('create2' in config.target.deterministicDeployment)
432
468
  )
433
469
  return create2Info;
434
- return config.network.deterministicDeployment.create2 || create2Info;
470
+ return config.target.deterministicDeployment.create2 || create2Info;
435
471
  })(),
436
472
  create3:
437
- config.network.deterministicDeployment &&
438
- 'create3' in config.network.deterministicDeployment &&
439
- config.network.deterministicDeployment.create3
440
- ? config.network.deterministicDeployment.create3
473
+ config.target.deterministicDeployment &&
474
+ 'create3' in config.target.deterministicDeployment &&
475
+ config.target.deterministicDeployment.create3
476
+ ? config.target.deterministicDeployment.create3
441
477
  : create3Info,
442
478
  };
443
479
 
@@ -450,20 +486,20 @@ export function resolveConfig<
450
486
  }
451
487
  }
452
488
 
453
- if (config.network.scripts) {
454
- if (typeof config.network.scripts === 'string') {
455
- scripts = [config.network.scripts];
489
+ if (config.target.scripts) {
490
+ if (typeof config.target.scripts === 'string') {
491
+ scripts = [config.target.scripts];
456
492
  } else {
457
- scripts = config.network.scripts;
493
+ scripts = config.target.scripts;
458
494
  }
459
495
  }
460
496
  const resolvedConfig: ResolvedConfig<NamedAccounts, Data> = {
461
497
  ...config,
462
- network: {...config.network, deterministicDeployment, pollingInterval: networkPollingInterval},
498
+ target: {...config.target, deterministicDeployment, pollingInterval: networkPollingInterval},
463
499
  deployments: config.deployments || 'deployments',
464
500
  scripts,
465
501
  tags: config.tags || [],
466
- networkTags: config.networkTags || [],
502
+ targetTags: config.targetTags || [],
467
503
  saveDeployments: config.saveDeployments,
468
504
  accounts: config.accounts || ({} as NamedAccounts),
469
505
  data: config.data || ({} as Data),
@@ -1,5 +1,7 @@
1
+ import {Address} from 'abitype';
1
2
  import type {
2
3
  Environment,
4
+ JSONTypePlusBigInt,
3
5
  UnknownDeployments,
4
6
  UnresolvedNetworkSpecificData,
5
7
  UnresolvedUnknownNamedAccounts,
@@ -80,3 +82,95 @@ export type EnhancedDeployScriptFunction<
80
82
  env: EnhancedEnvironment<NamedAccounts, Data, Deployments, Functions, Extra>,
81
83
  args?: ArgumentsTypes
82
84
  ) => Promise<void | boolean>;
85
+
86
+ type ChainBlockExplorer = {
87
+ name: string;
88
+ url: string;
89
+ apiUrl?: string | undefined;
90
+ };
91
+ type ChainContract = {
92
+ address: Address;
93
+ blockCreated?: number | undefined;
94
+ };
95
+
96
+ type ChainNativeCurrency = {
97
+ name: string;
98
+ /** 2-6 characters long */
99
+ symbol: string;
100
+ decimals: number;
101
+ };
102
+
103
+ type ChainRpcUrls = {
104
+ http: readonly string[];
105
+ webSocket?: readonly string[] | undefined;
106
+ };
107
+
108
+ /**
109
+ * @description Combines members of an intersection into a readable type.
110
+ *
111
+ * @see {@link https://twitter.com/mattpocockuk/status/1622730173446557697?s=20&t=NdpAcmEFXY01xkqU3KO0Mg}
112
+ * @example
113
+ * Prettify<{ a: string } & { b: string } & { c: number, d: bigint }>
114
+ * => { a: string, b: string, c: number, d: bigint }
115
+ */
116
+ type Prettify<T> = {
117
+ [K in keyof T]: T[K];
118
+ } & {};
119
+
120
+ export type ChainInfo = {
121
+ /** ID in number form */
122
+ id: number;
123
+ /** Human-readable name */
124
+ name: string;
125
+ /** Collection of block explorers */
126
+ blockExplorers?:
127
+ | {
128
+ [key: string]: ChainBlockExplorer;
129
+ default: ChainBlockExplorer;
130
+ }
131
+ | undefined;
132
+ /** Collection of contracts */
133
+ contracts?:
134
+ | Prettify<
135
+ {
136
+ [key: string]: ChainContract | {[sourceId: number]: ChainContract | undefined} | undefined;
137
+ } & {
138
+ ensRegistry?: ChainContract | undefined;
139
+ ensUniversalResolver?: ChainContract | undefined;
140
+ multicall3?: ChainContract | undefined;
141
+ }
142
+ >
143
+ | undefined;
144
+ /** Currency used by chain */
145
+ nativeCurrency: ChainNativeCurrency;
146
+ /** Collection of RPC endpoints */
147
+ rpcUrls: {
148
+ [key: string]: ChainRpcUrls;
149
+ default: ChainRpcUrls;
150
+ };
151
+ /** Source Chain ID (ie. the L1 chain) */
152
+ sourceId?: number | undefined;
153
+ /** Flag for test networks */
154
+ testnet?: boolean | undefined;
155
+
156
+ chainType: 'zksync' | 'op-stack' | 'celo' | 'default';
157
+
158
+ genesisHash?: string;
159
+
160
+ properties?: Record<string, JSONTypePlusBigInt>;
161
+
162
+ // this will bring in the following when reconstructed from the data above
163
+
164
+ // /** Custom chain data. */
165
+ // custom?: any;
166
+
167
+ // /**
168
+ // * Modifies how chain data structures (ie. Blocks, Transactions, etc)
169
+ // * are formatted & typed.
170
+ // */
171
+ // formatters?: any | undefined;
172
+ // /** Modifies how data (ie. Transactions) is serialized. */
173
+ // serializers?: any | undefined;
174
+ // /** Modifies how fees are derived. */
175
+ // fees?: any | undefined;
176
+ };