rocketh 0.14.9 → 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;
@@ -9,16 +9,25 @@ import type {
9
9
  Environment,
10
10
  JSONTypePlusBigInt,
11
11
  ResolvedConfig,
12
+ TargetConfig,
12
13
  UnknownDeployments,
13
14
  UnresolvedNetworkSpecificData,
14
15
  UnresolvedUnknownNamedAccounts,
15
16
  } from '../environment/types.js';
16
17
  import {createEnvironment, SignerProtocolFunction} from '../environment/index.js';
17
- import {DeployScriptFunction, DeployScriptModule, EnhancedDeployScriptFunction, EnhancedEnvironment} from './types.js';
18
+ import {
19
+ ChainInfo,
20
+ DeployScriptFunction,
21
+ DeployScriptModule,
22
+ EnhancedDeployScriptFunction,
23
+ EnhancedEnvironment,
24
+ } from './types.js';
18
25
  import {withEnvironment} from '../utils/extensions.js';
19
26
  import {logger, setLogLevel, spin} from '../internal/logging.js';
20
27
  import {getRoughGasPriceEstimate} from '../utils/eth.js';
21
28
  import {traverseMultipleDirectory} from '../utils/fs.js';
29
+ import {getChain, getChainByName} from '../environment/utils/chains.js';
30
+ import {JSONRPCHTTPProvider} from 'eip-1193-jsonrpc-provider';
22
31
 
23
32
  // @ts-ignore
24
33
  const tsImport = (path: string, opts: any) => (typeof Bun !== 'undefined' ? import(path) : tsImport_(path, opts));
@@ -147,7 +156,7 @@ export type UntypedEIP1193Provider = {
147
156
  };
148
157
 
149
158
  export type ConfigOptions<Extra extends Record<string, unknown> = Record<string, unknown>> = {
150
- network?: string | {fork: string};
159
+ target?: string | {fork: string};
151
160
  deployments?: string;
152
161
  scripts?: string | string[];
153
162
  tags?: string;
@@ -181,35 +190,30 @@ export type DeterministicDeploymentInfo =
181
190
  create3?: Create3DeterministicDeploymentInfo;
182
191
  };
183
192
 
184
- type Networks = {
185
- [name: string]: {
186
- rpcUrl?: string;
187
- tags?: string[];
188
- deterministicDeployment?: DeterministicDeploymentInfo;
189
- scripts?: string | string[];
190
- publicInfo?: {
191
- name: string;
192
- nativeCurrency: {
193
- name: string;
194
- symbol: string;
195
- decimals: number;
196
- };
197
- rpcUrls: {
198
- default: {
199
- http: string[];
200
- };
201
- };
202
- chainType?: string;
203
- };
204
- pollingInterval?: number;
205
- properties?: Record<string, JSONTypePlusBigInt>;
206
- };
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;
207
210
  };
208
211
  export type UserConfig<
209
212
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
210
213
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData
211
214
  > = {
212
- networks?: Networks;
215
+ targets: {[name: string]: DeploymentTargetConfig};
216
+ chains?: Chains;
213
217
  deployments?: string;
214
218
  scripts?: string | string[];
215
219
  accounts?: NamedAccounts;
@@ -218,11 +222,14 @@ export type UserConfig<
218
222
  defaultPollingInterval?: number;
219
223
  };
220
224
 
221
- export function transformUserConfig<
225
+ export async function transformUserConfig<
222
226
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
223
227
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
224
228
  Extra extends Record<string, unknown> = Record<string, unknown>
225
- >(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>> {
226
233
  if (configFile) {
227
234
  if (!options.deployments && configFile.deployments) {
228
235
  options.deployments = configFile.deployments;
@@ -232,121 +239,145 @@ export function transformUserConfig<
232
239
  }
233
240
  }
234
241
 
235
- const fromEnv = process.env['ETH_NODE_URI_' + options.network];
236
- const fork = typeof options.network !== 'string';
237
- let networkName = 'memory';
238
- if (options.network) {
239
- if (typeof options.network === 'string') {
240
- networkName = options.network;
241
- } else if ('fork' in options.network) {
242
- 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
+ }
243
270
  }
244
271
  }
245
272
 
246
- 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
+ }
247
305
 
248
- let networkTags: string[] =
249
- (configFile?.networks && (configFile?.networks[networkName]?.tags || configFile?.networks['default']?.tags)) ||
250
- defaultTags;
306
+ let targetTags: string[] = actualChainConfig?.tags || defaultTags;
251
307
 
252
- let networkScripts: string | string[] | undefined =
253
- (configFile?.networks &&
254
- (configFile?.networks[networkName]?.scripts || configFile?.networks['default']?.scripts)) ||
255
- undefined;
308
+ let networkScripts: string | string[] | undefined = targetConfig?.scripts;
256
309
 
257
310
  // no default for publicInfo
258
- const publicInfo = configFile?.networks ? configFile?.networks[networkName]?.publicInfo : undefined;
259
311
  const defaultPollingInterval = configFile?.defaultPollingInterval;
260
- const pollingInterval = configFile?.networks?.[networkName]?.pollingInterval;
261
- const deterministicDeployment = configFile?.networks?.[networkName]?.deterministicDeployment;
262
- const properties = configFile?.networks ? configFile?.networks[networkName]?.properties : undefined;
312
+ const pollingInterval = actualChainConfig?.pollingInterval;
313
+ const deterministicDeployment = actualChainConfig?.deterministicDeployment;
314
+ const properties = actualChainConfig?.properties;
315
+
316
+ let resolvedTargetConfig: TargetConfig;
317
+
263
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];
264
325
  let nodeUrl: string;
265
- if (typeof fromEnv === 'string') {
326
+ if (typeof fromEnv === 'string' && fromEnv) {
266
327
  nodeUrl = fromEnv;
267
328
  } else {
268
- if (configFile) {
269
- const network = configFile.networks && configFile.networks[networkName];
270
- if (network && network.rpcUrl) {
271
- nodeUrl = network.rpcUrl;
272
- } else {
273
- if (options?.ignoreMissingRPC) {
274
- nodeUrl = '';
275
- } else {
276
- if (options.network === 'localhost') {
277
- nodeUrl = 'http://127.0.0.1:8545';
278
- } else {
279
- console.error(`network "${options.network}" is not configured. Please add it to the rocketh.js/ts file`);
280
- process.exit(1);
281
- }
282
- }
283
- }
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';
284
335
  } else {
285
- if (options?.ignoreMissingRPC) {
286
- nodeUrl = '';
287
- } else {
288
- if (options.network === 'localhost') {
289
- nodeUrl = 'http://127.0.0.1:8545';
290
- } else {
291
- console.error(`network "${options.network}" is not configured. Please add it to the rocketh.js/ts file`);
292
- process.exit(1);
293
- }
294
- }
336
+ console.error(`network "${options.target}" is not configured. Please add it to the rocketh.js/ts file`);
337
+ process.exit(1);
295
338
  }
296
339
  }
297
340
 
298
- return {
299
- network: {
300
- nodeUrl,
301
- name: networkName,
302
- tags: networkTags,
303
- fork,
304
- deterministicDeployment,
305
- scripts: networkScripts,
306
- publicInfo,
307
- pollingInterval,
308
- properties,
309
- },
310
- deployments: options.deployments,
311
- saveDeployments: options.saveDeployments,
312
- scripts: options.scripts,
313
- data: configFile?.data,
314
- tags: typeof options.tags === 'undefined' ? undefined : options.tags.split(','),
315
- logLevel: options.logLevel,
316
- askBeforeProceeding: options.askBeforeProceeding,
317
- reportGasUse: options.reportGasUse,
318
- accounts: configFile?.accounts,
319
- signerProtocols: configFile?.signerProtocols,
320
- extra: options.extra,
321
- defaultPollingInterval,
341
+ resolvedTargetConfig = {
342
+ nodeUrl,
343
+ name: targetName,
344
+ tags: targetTags,
345
+ fork,
346
+ deterministicDeployment,
347
+ scripts: networkScripts,
348
+ chainInfo,
349
+ pollingInterval,
350
+ properties,
322
351
  };
323
352
  } else {
324
- return {
325
- network: {
326
- provider: options.provider as EIP1193ProviderWithoutEvents,
327
- name: networkName,
328
- tags: networkTags,
329
- fork,
330
- deterministicDeployment,
331
- scripts: networkScripts,
332
- publicInfo,
333
- pollingInterval,
334
- properties,
335
- },
336
- deployments: options.deployments,
337
- saveDeployments: options.saveDeployments,
338
- scripts: options.scripts,
339
- data: configFile?.data,
340
- tags: typeof options.tags === 'undefined' ? undefined : options.tags.split(','),
341
- logLevel: options.logLevel,
342
- askBeforeProceeding: options.askBeforeProceeding,
343
- reportGasUse: options.reportGasUse,
344
- accounts: configFile?.accounts,
345
- signerProtocols: configFile?.signerProtocols,
346
- extra: options.extra,
347
- 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,
348
363
  };
349
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
+ };
350
381
  }
351
382
 
352
383
  export async function readConfig<
@@ -423,26 +454,26 @@ export function resolveConfig<
423
454
  } as const;
424
455
 
425
456
  const defaultPollingInterval = config.defaultPollingInterval || 1;
426
- const networkPollingInterval = config.network.pollingInterval || defaultPollingInterval;
457
+ const networkPollingInterval = config.target.pollingInterval || defaultPollingInterval;
427
458
 
428
459
  let deterministicDeployment: {
429
460
  create2: Create2DeterministicDeploymentInfo;
430
461
  create3: Create3DeterministicDeploymentInfo;
431
462
  } = {
432
463
  create2: (() => {
433
- if (!config.network.deterministicDeployment) return create2Info;
464
+ if (!config.target.deterministicDeployment) return create2Info;
434
465
  if (
435
- !('create3' in config.network.deterministicDeployment) &&
436
- !('create2' in config.network.deterministicDeployment)
466
+ !('create3' in config.target.deterministicDeployment) &&
467
+ !('create2' in config.target.deterministicDeployment)
437
468
  )
438
469
  return create2Info;
439
- return config.network.deterministicDeployment.create2 || create2Info;
470
+ return config.target.deterministicDeployment.create2 || create2Info;
440
471
  })(),
441
472
  create3:
442
- config.network.deterministicDeployment &&
443
- 'create3' in config.network.deterministicDeployment &&
444
- config.network.deterministicDeployment.create3
445
- ? config.network.deterministicDeployment.create3
473
+ config.target.deterministicDeployment &&
474
+ 'create3' in config.target.deterministicDeployment &&
475
+ config.target.deterministicDeployment.create3
476
+ ? config.target.deterministicDeployment.create3
446
477
  : create3Info,
447
478
  };
448
479
 
@@ -455,20 +486,20 @@ export function resolveConfig<
455
486
  }
456
487
  }
457
488
 
458
- if (config.network.scripts) {
459
- if (typeof config.network.scripts === 'string') {
460
- scripts = [config.network.scripts];
489
+ if (config.target.scripts) {
490
+ if (typeof config.target.scripts === 'string') {
491
+ scripts = [config.target.scripts];
461
492
  } else {
462
- scripts = config.network.scripts;
493
+ scripts = config.target.scripts;
463
494
  }
464
495
  }
465
496
  const resolvedConfig: ResolvedConfig<NamedAccounts, Data> = {
466
497
  ...config,
467
- network: {...config.network, deterministicDeployment, pollingInterval: networkPollingInterval},
498
+ target: {...config.target, deterministicDeployment, pollingInterval: networkPollingInterval},
468
499
  deployments: config.deployments || 'deployments',
469
500
  scripts,
470
501
  tags: config.tags || [],
471
- networkTags: config.networkTags || [],
502
+ targetTags: config.targetTags || [],
472
503
  saveDeployments: config.saveDeployments,
473
504
  accounts: config.accounts || ({} as NamedAccounts),
474
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
+ };