rocketh 0.9.1 → 0.10.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.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { EIP1193Account, EIP1193DATA, EIP1193QUANTITY, EIP1193SignerProvider, EIP1193ProviderWithoutEvents, EIP1193WalletProvider, EIP1193TransactionReceipt } from 'eip-1193';
1
+ import { EIP1193Account, EIP1193DATA, EIP1193QUANTITY, EIP1193SignerProvider, EIP1193ProviderWithoutEvents, EIP1193WalletProvider, EIP1193TransactionReceipt, EIP1193GenericRequestProvider } from 'eip-1193';
2
+ import * as abitype from 'abitype';
2
3
  import { Abi, Narrow } from 'abitype';
3
4
  export { Abi, AbiConstructor, AbiError, AbiEvent, AbiFallback, AbiFunction, AbiReceive } from 'abitype';
4
- import { Chain, DeployContractParameters } from 'viem';
5
+ import { Chain, Address, DeployContractParameters } from 'viem';
6
+ import { Chain as Chain$1 } from 'viem/chains';
5
7
 
6
8
  type ProgressIndicator = {
7
9
  start(msg?: string): ProgressIndicator;
@@ -195,27 +197,38 @@ type Context<Artifacts extends UnknownArtifacts = UnknownArtifacts, NamedAccount
195
197
  accounts: NamedAccounts;
196
198
  artifacts: Artifacts;
197
199
  };
198
- type BaseConfig = {
199
- networkName?: string;
200
+ type NetworkConfigBase = {
201
+ name: string;
202
+ tags: string[];
203
+ fork?: boolean;
204
+ };
205
+ type NetworkConfigForJSONRPC = NetworkConfigBase & {
206
+ nodeUrl: string;
207
+ };
208
+ type NetworkConfigForEIP1193Provider = NetworkConfigBase & {
209
+ provider: EIP1193ProviderWithoutEvents;
210
+ };
211
+ type NetworkConfig = NetworkConfigForJSONRPC | NetworkConfigForEIP1193Provider;
212
+ type Config = {
213
+ network: NetworkConfig;
214
+ networkTags?: string[];
200
215
  scripts?: string;
201
216
  deployments?: string;
217
+ saveDeployments?: boolean;
202
218
  tags?: string[];
203
219
  logLevel?: number;
204
220
  gasPricing?: {};
205
221
  };
206
- type ConfigForJSONRPC = BaseConfig & {
207
- networkName: string;
208
- nodeUrl: string;
209
- };
210
- type ConfigForEIP1193Provider = BaseConfig & {
211
- provider: EIP1193ProviderWithoutEvents;
212
- };
213
- type Config = ConfigForJSONRPC | ConfigForEIP1193Provider;
214
222
  type ResolvedConfig = Config & {
215
223
  deployments: string;
216
224
  scripts: string;
217
225
  tags: string[];
218
- networkName: string;
226
+ network: {
227
+ name: string;
228
+ tags: string[];
229
+ fork?: boolean;
230
+ };
231
+ saveDeployments?: boolean;
219
232
  };
220
233
  interface Environment<Artifacts extends UnknownArtifacts = UnknownArtifacts, NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts, Deployments extends UnknownDeployments = UnknownDeployments> {
221
234
  config: ResolvedConfig;
@@ -238,7 +251,15 @@ interface Environment<Artifacts extends UnknownArtifacts = UnknownArtifacts, Nam
238
251
  savePendingDeployment<TAbi extends Abi = Abi>(pendingDeployment: PendingDeployment<TAbi>): Promise<Deployment<TAbi>>;
239
252
  savePendingExecution(pendingExecution: PendingExecution): Promise<EIP1193TransactionReceipt>;
240
253
  get<TAbi extends Abi>(name: string): Deployment<TAbi>;
241
- getOrNull<TAbi extends Abi>(name: string): Deployment<TAbi> | undefined;
254
+ getOrNull<TAbi extends Abi>(name: string): Deployment<TAbi> | null;
255
+ fromAddressToNamedABI<TAbi extends Abi>(address: Address): {
256
+ mergedABI: TAbi;
257
+ names: string[];
258
+ };
259
+ fromAddressToNamedABIOrNull<TAbi extends Abi>(address: Address): {
260
+ mergedABI: TAbi;
261
+ names: string[];
262
+ } | null;
242
263
  showMessage(message: string): void;
243
264
  showProgress(message?: string): ProgressIndicator;
244
265
  }
@@ -294,20 +315,22 @@ declare function execute<Artifacts extends UnknownArtifacts = UnknownArtifacts,
294
315
  dependencies?: string[];
295
316
  }): DeployScriptModule<Artifacts, NamedAccounts, ArgumentsType, Deployments>;
296
317
  type ConfigOptions = {
297
- network: string;
318
+ network?: string | {
319
+ fork: string;
320
+ };
298
321
  deployments?: string;
299
322
  scripts?: string;
300
323
  tags?: string;
301
- };
302
- declare function readConfig(options: ConfigOptions, extra?: {
303
- ignoreMissingRPC?: boolean;
304
- }): Config;
305
- declare function readAndResolveConfig(options: ConfigOptions, extra?: {
324
+ logLevel?: number;
325
+ provider?: EIP1193ProviderWithoutEvents | EIP1193GenericRequestProvider;
306
326
  ignoreMissingRPC?: boolean;
307
- }): ResolvedConfig;
327
+ saveDeployments?: boolean;
328
+ };
329
+ declare function readConfig(options: ConfigOptions): Config;
330
+ declare function readAndResolveConfig(options: ConfigOptions): ResolvedConfig;
308
331
  declare function resolveConfig(config: Config): ResolvedConfig;
309
- declare function loadEnvironment<Artifacts extends UnknownArtifacts = UnknownArtifacts, NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts>(config: Config, context: ProvidedContext<Artifacts, NamedAccounts>): Promise<Environment>;
310
- declare function loadAndExecuteDeployments<Artifacts extends UnknownArtifacts = UnknownArtifacts, NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts, ArgumentsType = undefined, Deployments extends UnknownDeployments = UnknownDeployments>(config: Config, args?: ArgumentsType): Promise<Environment>;
332
+ declare function loadEnvironment<Artifacts extends UnknownArtifacts = UnknownArtifacts, NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts>(options: ConfigOptions, context: ProvidedContext<Artifacts, NamedAccounts>): Promise<Environment>;
333
+ declare function loadAndExecuteDeployments<Artifacts extends UnknownArtifacts = UnknownArtifacts, NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts, ArgumentsType = undefined, Deployments extends UnknownDeployments = UnknownDeployments>(options: ConfigOptions, args?: ArgumentsType): Promise<Environment>;
311
334
  declare function executeDeployScripts<Artifacts extends UnknownArtifacts = UnknownArtifacts, NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts, ArgumentsType = undefined, Deployments extends UnknownDeployments = UnknownDeployments>(config: ResolvedConfig, args?: ArgumentsType): Promise<Environment>;
312
335
 
313
336
  type EnvironmentExtenstion = (env: Environment) => Environment;
@@ -315,7 +338,7 @@ declare function extendEnvironment(extension: EnvironmentExtenstion): void;
315
338
  type SignerProtocolFunction = (protocolString: string) => Promise<NamedSigner>;
316
339
  declare function handleSignerProtocol(protocol: string, getSigner: SignerProtocolFunction): void;
317
340
 
318
- declare function loadDeployments(deploymentsPath: string, subPath: string, onlyABIAndAddress?: boolean, expectedChain?: {
341
+ declare function loadDeployments(deploymentsPath: string, networkName: string, onlyABIAndAddress?: boolean, expectedChain?: {
319
342
  chainId: string;
320
343
  genesisHash?: `0x${string}`;
321
344
  deleteDeploymentsIfDifferentGenesisHash?: boolean;
@@ -325,4 +348,27 @@ declare function loadDeployments(deploymentsPath: string, subPath: string, onlyA
325
348
  genesisHash?: `0x${string}`;
326
349
  };
327
350
 
328
- export { type AccountDefinition, type AccountType, type Artifact, type Config, type ConfigOptions, type Context, type CreationGasEstimate, type DeployScriptFunction, type DeployScriptModule, type Deployment, type DeploymentConstruction, type DevDoc, type DevErrorDoc, type DevEventDoc, type DevMethodDoc, type Environment, type GasEstimate, type GasEstimates, type Libraries, type NamedSigner, type NoticeUserDoc, type PartialDeployment, type PendingDeployment, type PendingExecution, type PendingTransaction, type ProvidedContext, type ResolvedAccount, type ResolvedConfig, type ResolvedNamedAccounts, type ResolvedNamedSigners, type ScriptCallback, type Storage, type StorageLayout, type TypeDef, type UnknownArtifacts, type UnknownDeployments, type UnknownDeploymentsAcrossNetworks, type UnknownNamedAccounts, type UnresolvedUnknownNamedAccounts, type UserDoc, execute, executeDeployScripts, extendEnvironment, handleSignerProtocol, loadAndExecuteDeployments, loadDeployments, loadEnvironment, readAndResolveConfig, readConfig, resolveConfig };
351
+ type CreateMutable<Type> = {
352
+ -readonly [Property in keyof Type]: Type[Property];
353
+ };
354
+ declare function mergeArtifacts(list: {
355
+ name: string;
356
+ artifact: Artifact<Abi>;
357
+ }[]): {
358
+ mergedABI: (abitype.AbiConstructor | abitype.AbiError | abitype.AbiEvent | abitype.AbiFallback | abitype.AbiFunction | abitype.AbiReceive)[];
359
+ added: Map<string, abitype.AbiConstructor | abitype.AbiError | abitype.AbiEvent | abitype.AbiFallback | abitype.AbiFunction | abitype.AbiReceive>;
360
+ mergedDevDocs: CreateMutable<DevDoc>;
361
+ mergedUserDocs: CreateMutable<UserDoc>;
362
+ sigJSMap: Map<`0x${string}`, {
363
+ index: number;
364
+ routeName: string;
365
+ functionName: string;
366
+ }>;
367
+ };
368
+
369
+ declare const chainById: {
370
+ [chainId: string]: Chain$1;
371
+ };
372
+ declare function getChain(id: string): Chain$1;
373
+
374
+ export { type AccountDefinition, type AccountType, type Artifact, type Config, type ConfigOptions, type Context, type CreationGasEstimate, type DeployScriptFunction, type DeployScriptModule, type Deployment, type DeploymentConstruction, type DevDoc, type DevErrorDoc, type DevEventDoc, type DevMethodDoc, type Environment, type GasEstimate, type GasEstimates, type Libraries, type NamedSigner, type NetworkConfig, type NoticeUserDoc, type PartialDeployment, type PendingDeployment, type PendingExecution, type PendingTransaction, type ProvidedContext, type ResolvedAccount, type ResolvedConfig, type ResolvedNamedAccounts, type ResolvedNamedSigners, type ScriptCallback, type Storage, type StorageLayout, type TypeDef, type UnknownArtifacts, type UnknownDeployments, type UnknownDeploymentsAcrossNetworks, type UnknownNamedAccounts, type UnresolvedUnknownNamedAccounts, type UserDoc, chainById, execute, executeDeployScripts, extendEnvironment, getChain, handleSignerProtocol, loadAndExecuteDeployments, loadDeployments, loadEnvironment, mergeArtifacts, readAndResolveConfig, readConfig, resolveConfig };
package/dist/index.js CHANGED
@@ -1,23 +1,29 @@
1
1
  import {
2
+ chainById,
2
3
  execute,
3
4
  executeDeployScripts,
4
5
  extendEnvironment,
6
+ getChain,
5
7
  handleSignerProtocol,
6
8
  loadAndExecuteDeployments,
7
9
  loadDeployments,
8
10
  loadEnvironment,
11
+ mergeArtifacts,
9
12
  readAndResolveConfig,
10
13
  readConfig,
11
14
  resolveConfig
12
- } from "./chunk-LPKP3ACG.js";
15
+ } from "./chunk-4RQLWJEN.js";
13
16
  export {
17
+ chainById,
14
18
  execute,
15
19
  executeDeployScripts,
16
20
  extendEnvironment,
21
+ getChain,
17
22
  handleSignerProtocol,
18
23
  loadAndExecuteDeployments,
19
24
  loadDeployments,
20
25
  loadEnvironment,
26
+ mergeArtifacts,
21
27
  readAndResolveConfig,
22
28
  readConfig,
23
29
  resolveConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rocketh",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "deploy smart contract on ethereum-compatible networks",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -28,6 +28,7 @@
28
28
  "eip-1193-jsonrpc-provider": "^0.3.0",
29
29
  "esbuild": "^0.20.1",
30
30
  "esbuild-register": "^3.5.0",
31
+ "ethers": "^6.11.1",
31
32
  "figlet": "^1.7.0",
32
33
  "ldenv": "^0.3.9",
33
34
  "named-logs": "^0.2.2",
package/src/cli.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #! /usr/bin/env node
2
2
  import {loadEnv} from 'ldenv';
3
- import {loadAndExecuteDeployments, readConfig} from '.';
3
+ import {ConfigOptions, loadAndExecuteDeployments, readConfig} from '.';
4
4
  import {Command} from 'commander';
5
5
  import pkg from '../package.json';
6
6
 
@@ -20,6 +20,5 @@ program
20
20
  .parse(process.argv);
21
21
 
22
22
  const options = program.opts();
23
- const config = readConfig(options as any);
24
23
 
25
- loadAndExecuteDeployments({...config, logLevel: 1});
24
+ loadAndExecuteDeployments({...(options as ConfigOptions), logLevel: 1});
@@ -5,12 +5,12 @@ import {UnknownDeployments} from './types';
5
5
 
6
6
  export function loadDeployments(
7
7
  deploymentsPath: string,
8
- subPath: string,
8
+ networkName: string,
9
9
  onlyABIAndAddress?: boolean,
10
10
  expectedChain?: {chainId: string; genesisHash?: `0x${string}`; deleteDeploymentsIfDifferentGenesisHash?: boolean}
11
11
  ): {deployments: UnknownDeployments; chainId?: string; genesisHash?: `0x${string}`} {
12
12
  const deploymentsFound: UnknownDeployments = {};
13
- const deployPath = path.join(deploymentsPath, subPath);
13
+ const deployPath = path.join(deploymentsPath, networkName);
14
14
 
15
15
  let filesStats;
16
16
  try {
@@ -34,7 +34,7 @@ export function loadDeployments(
34
34
  genesisHash = chainData.genesisHash;
35
35
  } else {
36
36
  throw new Error(
37
- `A '.chain' or '.chainId' file is expected to be present in the deployment folder for network ${subPath}`
37
+ `A '.chain' or '.chainId' file is expected to be present in the deployment folder for network ${networkName}`
38
38
  );
39
39
  }
40
40
  }
@@ -4,6 +4,7 @@ import {TransactionReceipt, createPublicClient, custom} from 'viem';
4
4
 
5
5
  import {
6
6
  AccountType,
7
+ Artifact,
7
8
  Deployment,
8
9
  Environment,
9
10
  NamedSigner,
@@ -18,7 +19,7 @@ import {
18
19
  UnresolvedUnknownNamedAccounts,
19
20
  } from './types';
20
21
  import {JSONRPCHTTPProvider} from 'eip-1193-jsonrpc-provider';
21
- import {Abi} from 'abitype';
22
+ import {Abi, Address} from 'abitype';
22
23
  import {InternalEnvironment} from '../internal/types';
23
24
  import path from 'node:path';
24
25
  import {JSONToString, stringToJSON} from '../utils/json';
@@ -35,6 +36,7 @@ import {ProvidedContext} from '../executor/types';
35
36
  import {ProgressIndicator, log, spin} from '../internal/logging';
36
37
  import {PendingExecution} from './types';
37
38
  import {getChain} from './utils/chains';
39
+ import {mergeArtifacts} from './utils/artifacts';
38
40
 
39
41
  type ReceiptResult = {receipt: EIP1193TransactionReceipt; latestBlockNumber: EIP1193QUANTITY};
40
42
 
@@ -83,7 +85,9 @@ export async function createEnvironment<
83
85
  providedContext: ProvidedContext<Artifacts, NamedAccounts>
84
86
  ): Promise<{internal: InternalEnvironment; external: Environment<Artifacts, NamedAccounts, Deployments>}> {
85
87
  const provider =
86
- 'provider' in config ? config.provider : (new JSONRPCHTTPProvider(config.nodeUrl) as EIP1193ProviderWithoutEvents);
88
+ 'provider' in config.network
89
+ ? config.network.provider
90
+ : (new JSONRPCHTTPProvider(config.network.nodeUrl) as EIP1193ProviderWithoutEvents);
87
91
 
88
92
  const transport = custom(provider);
89
93
  const viemClient = createPublicClient({transport});
@@ -98,24 +102,32 @@ export async function createEnvironment<
98
102
 
99
103
  let networkName: string;
100
104
  let saveDeployments: boolean;
101
- let tags: {[tag: string]: boolean} = {};
105
+ let networkTags: {[tag: string]: boolean} = {};
106
+ for (const networkTag of config.network.tags) {
107
+ networkTags[networkTag] = true;
108
+ }
109
+
102
110
  if ('nodeUrl' in config) {
103
- networkName = config.networkName;
111
+ networkName = config.network.name;
104
112
  saveDeployments = true;
105
113
  } else {
106
- if (config.networkName) {
107
- networkName = config.networkName;
114
+ if (config.network.name) {
115
+ networkName = config.network.name;
108
116
  } else {
109
117
  networkName = 'memory';
110
118
  }
111
119
  if (networkName === 'memory' || networkName === 'hardhat') {
112
- tags['memory'] = true;
120
+ networkTags['memory'] = true;
113
121
  saveDeployments = false;
114
122
  } else {
115
123
  saveDeployments = true;
116
124
  }
117
125
  }
118
126
 
127
+ if (config.saveDeployments !== undefined) {
128
+ saveDeployments = config.saveDeployments;
129
+ }
130
+
119
131
  const resolvedAccounts: {[name: string]: ResolvedAccount} = {};
120
132
 
121
133
  const accountCache: {[name: string]: ResolvedAccount} = {};
@@ -203,16 +215,26 @@ export async function createEnvironment<
203
215
  artifacts: providedContext.artifacts as Artifacts,
204
216
  network: {
205
217
  name: networkName,
218
+ fork: config.network.fork,
206
219
  saveDeployments,
207
- tags,
220
+ tags: networkTags,
208
221
  },
209
222
  };
210
223
 
211
- const {deployments} = loadDeployments(config.deployments, context.network.name, false, {
212
- chainId,
213
- genesisHash,
214
- deleteDeploymentsIfDifferentGenesisHash: true,
215
- });
224
+ // console.log(`context`, JSON.stringify(context.network, null, 2));
225
+
226
+ const {deployments} = loadDeployments(
227
+ config.deployments,
228
+ context.network.name,
229
+ false,
230
+ context.network.fork
231
+ ? undefined
232
+ : {
233
+ chainId,
234
+ genesisHash,
235
+ deleteDeploymentsIfDifferentGenesisHash: true,
236
+ }
237
+ );
216
238
 
217
239
  const namedAccounts: {[name: string]: EIP1193Account} = {};
218
240
  const namedSigners: {[name: string]: NamedSigner} = {};
@@ -280,8 +302,35 @@ export async function createEnvironment<
280
302
  return deployment;
281
303
  }
282
304
 
283
- function getOrNull<TAbi extends Abi>(name: string): Deployment<TAbi> | undefined {
284
- return deployments[name] as Deployment<TAbi> | undefined;
305
+ function getOrNull<TAbi extends Abi>(name: string): Deployment<TAbi> | null {
306
+ return (deployments[name] || null) as Deployment<TAbi> | null;
307
+ }
308
+
309
+ function fromAddressToNamedABIOrNull<TAbi extends Abi>(address: Address): {mergedABI: TAbi; names: string[]} | null {
310
+ let list: {name: string; artifact: Artifact<Abi>}[] = [];
311
+ for (const name of Object.keys(deployments)) {
312
+ const deployment = deployments[name];
313
+ if (deployment.address.toLowerCase() == address.toLowerCase()) {
314
+ list.push({name, artifact: deployment});
315
+ }
316
+ }
317
+ if (list.length === 0) {
318
+ return null;
319
+ }
320
+
321
+ const {mergedABI} = mergeArtifacts(list);
322
+ return {
323
+ mergedABI: mergedABI as unknown as TAbi,
324
+ names: list.map((v) => v.name),
325
+ };
326
+ }
327
+
328
+ function fromAddressToNamedABI<TAbi extends Abi>(address: Address): {mergedABI: TAbi; names: string[]} {
329
+ const n = fromAddressToNamedABIOrNull<TAbi>(address);
330
+ if (!n) {
331
+ throw new Error(`could not find artifact for address ${address}`);
332
+ }
333
+ return n;
285
334
  }
286
335
 
287
336
  async function save<TAbi extends Abi>(name: string, deployment: Deployment<TAbi>): Promise<Deployment<TAbi>> {
@@ -594,6 +643,8 @@ export async function createEnvironment<
594
643
  savePendingExecution,
595
644
  get,
596
645
  getOrNull,
646
+ fromAddressToNamedABI,
647
+ fromAddressToNamedABIOrNull,
597
648
  showMessage,
598
649
  showProgress,
599
650
  };
@@ -9,7 +9,7 @@ import {
9
9
  EIP1193WalletProvider,
10
10
  } from 'eip-1193';
11
11
  import {Abi, Narrow, AbiError, AbiEvent, AbiConstructor, AbiFallback, AbiFunction, AbiReceive} from 'abitype';
12
- import type {DeployContractParameters} from 'viem';
12
+ import type {Address, DeployContractParameters} from 'viem';
13
13
  import type {Chain} from 'viem';
14
14
  import {ProgressIndicator} from '../internal/logging';
15
15
 
@@ -209,30 +209,47 @@ export type Context<
209
209
  artifacts: Artifacts;
210
210
  };
211
211
 
212
- type BaseConfig = {
213
- networkName?: string;
212
+ type NetworkConfigBase = {
213
+ name: string;
214
+ tags: string[];
215
+ fork?: boolean;
216
+ };
217
+ type NetworkConfigForJSONRPC = NetworkConfigBase & {
218
+ nodeUrl: string;
219
+ };
220
+
221
+ type NetworkConfigForEIP1193Provider = NetworkConfigBase & {
222
+ provider: EIP1193ProviderWithoutEvents;
223
+ };
224
+
225
+ export type NetworkConfig = NetworkConfigForJSONRPC | NetworkConfigForEIP1193Provider;
226
+
227
+ export type Config = {
228
+ network: NetworkConfig;
229
+ networkTags?: string[];
214
230
  scripts?: string;
215
231
  deployments?: string;
232
+ saveDeployments?: boolean;
216
233
 
217
234
  tags?: string[];
235
+
218
236
  logLevel?: number;
219
237
  // TODO
220
238
  gasPricing?: {};
221
239
  };
222
240
 
223
- type ConfigForJSONRPC = BaseConfig & {
224
- networkName: string;
225
- nodeUrl: string;
226
- };
227
-
228
- type ConfigForEIP1193Provider = BaseConfig & {
229
- provider: EIP1193ProviderWithoutEvents;
241
+ export type ResolvedConfig = Config & {
242
+ deployments: string;
243
+ scripts: string;
244
+ tags: string[];
245
+ network: {
246
+ name: string;
247
+ tags: string[];
248
+ fork?: boolean;
249
+ };
250
+ saveDeployments?: boolean;
230
251
  };
231
252
 
232
- export type Config = ConfigForJSONRPC | ConfigForEIP1193Provider;
233
-
234
- export type ResolvedConfig = Config & {deployments: string; scripts: string; tags: string[]; networkName: string};
235
-
236
253
  export interface Environment<
237
254
  Artifacts extends UnknownArtifacts = UnknownArtifacts,
238
255
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
@@ -254,7 +271,9 @@ export interface Environment<
254
271
  savePendingDeployment<TAbi extends Abi = Abi>(pendingDeployment: PendingDeployment<TAbi>): Promise<Deployment<TAbi>>;
255
272
  savePendingExecution(pendingExecution: PendingExecution): Promise<EIP1193TransactionReceipt>;
256
273
  get<TAbi extends Abi>(name: string): Deployment<TAbi>;
257
- getOrNull<TAbi extends Abi>(name: string): Deployment<TAbi> | undefined;
274
+ getOrNull<TAbi extends Abi>(name: string): Deployment<TAbi> | null;
275
+ fromAddressToNamedABI<TAbi extends Abi>(address: Address): {mergedABI: TAbi; names: string[]};
276
+ fromAddressToNamedABIOrNull<TAbi extends Abi>(address: Address): {mergedABI: TAbi; names: string[]} | null;
258
277
  showMessage(message: string): void;
259
278
  showProgress(message?: string): ProgressIndicator;
260
279
  }
@@ -0,0 +1,151 @@
1
+ import {Abi} from 'abitype';
2
+ import {Artifact, DevDoc, UserDoc} from '../types';
3
+ import {FunctionFragment} from 'ethers';
4
+
5
+ type CreateMutable<Type> = {
6
+ -readonly [Property in keyof Type]: Type[Property];
7
+ };
8
+
9
+ type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer ElementType)[]
10
+ ? ElementType
11
+ : never;
12
+
13
+ // from https://gist.github.com/egardner/efd34f270cc33db67c0246e837689cb9
14
+ function deepEqual(obj1: any, obj2: any): boolean {
15
+ // Private
16
+ function isObject(obj: any) {
17
+ if (typeof obj === 'object' && obj != null) {
18
+ return true;
19
+ } else {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ if (obj1 === obj2) {
25
+ return true;
26
+ } else if (isObject(obj1) && isObject(obj2)) {
27
+ if (Object.keys(obj1).length !== Object.keys(obj2).length) {
28
+ return false;
29
+ }
30
+ for (var prop in obj1) {
31
+ if (!deepEqual(obj1[prop], obj2[prop])) {
32
+ return false;
33
+ }
34
+ }
35
+ return true;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ function mergeDoc(values: any, mergedDevDocs: any, field: string) {
41
+ if (values[field]) {
42
+ const mergedEventDocs = (mergedDevDocs[field] = mergedDevDocs[field] || {});
43
+ for (const signature of Object.keys(values[field])) {
44
+ if (mergedEventDocs[signature] && !deepEqual(mergedEventDocs[signature], values[field][signature])) {
45
+ throw new Error(`Doc ${field} conflict: "${signature}" `);
46
+ }
47
+ mergedEventDocs[signature] = values[field][signature];
48
+ }
49
+ }
50
+ }
51
+
52
+ export function mergeArtifacts(list: {name: string; artifact: Artifact<Abi>}[]) {
53
+ const mergedABI: CreateMutable<Abi> = [];
54
+ const added: Map<string, ArrayElement<Abi>> = new Map();
55
+ const mergedDevDocs: CreateMutable<DevDoc> = {kind: 'dev', version: 1, methods: {}};
56
+ const mergedUserDocs: CreateMutable<UserDoc> = {kind: 'user', version: 1, methods: {}};
57
+ const sigJSMap: Map<`0x${string}`, {index: number; routeName: string; functionName: string}> = new Map();
58
+
59
+ for (let i = 0; i < list.length; i++) {
60
+ const listElem = list[i];
61
+ for (const element of listElem.artifact.abi) {
62
+ if (element.type === 'function') {
63
+ // const selector = getFunctionSelector(element);
64
+ const selector = FunctionFragment.from(element).selector as `0x${string}`;
65
+ if (sigJSMap.has(selector)) {
66
+ const existing = sigJSMap.get(selector);
67
+ throw new Error(
68
+ `ABI conflict: ${existing!.routeName} has function "${existing!.functionName}" which conflict with ${
69
+ listElem.name
70
+ }'s "${element.name}" (selector: "${selector}") `
71
+ );
72
+ }
73
+ sigJSMap.set(selector, {index: i, routeName: listElem.name, functionName: element.name});
74
+
75
+ const exists = added.has(element.name);
76
+ if (exists) {
77
+ // TODO check if same
78
+ } else {
79
+ added.set(element.name, element);
80
+ mergedABI.push(element);
81
+ }
82
+ } else if (element.type === 'constructor') {
83
+ // we skip it
84
+ } else if (element.type === 'error') {
85
+ const exists = added.has(element.name);
86
+ if (exists) {
87
+ // TODO check if same
88
+ } else {
89
+ added.set(element.name, element);
90
+ mergedABI.push(element);
91
+ }
92
+ } else if (element.type === 'event') {
93
+ const exists = added.has(element.name);
94
+ if (exists) {
95
+ // TODO check if same
96
+ } else {
97
+ added.set(element.name, element);
98
+ mergedABI.push(element);
99
+ }
100
+ } else if (element.type === 'fallback') {
101
+ } else if (element.type === 'receive') {
102
+ } else {
103
+ // if ('name' in element) {
104
+ // const exists = added.has(element.name);
105
+ // if (exists) {
106
+ // // TODO check if same
107
+ // } else {
108
+ // added.set(element.name, element);
109
+ // mergedABI.push(element);
110
+ // }
111
+ // }
112
+ }
113
+ }
114
+ const devdoc = listElem.artifact.devdoc;
115
+ if (devdoc) {
116
+ mergeDoc(devdoc, mergedDevDocs, 'events');
117
+ mergeDoc(devdoc, mergedDevDocs, 'errors');
118
+ mergeDoc(devdoc, mergedDevDocs, 'methods');
119
+ if (devdoc.author) {
120
+ if (mergedDevDocs.author && mergedDevDocs.author != devdoc.author) {
121
+ throw new Error(`DevDoc author conflict `);
122
+ }
123
+ mergedDevDocs.author = devdoc.author;
124
+ if (mergedDevDocs.title && mergedDevDocs.title != devdoc.title) {
125
+ throw new Error(`DevDoc title conflict `);
126
+ }
127
+ mergedDevDocs.title = devdoc.title;
128
+ }
129
+ }
130
+
131
+ const userdoc = listElem.artifact.userdoc;
132
+ if (userdoc) {
133
+ mergeDoc(userdoc, mergedUserDocs, 'events');
134
+ mergeDoc(userdoc, mergedUserDocs, 'errors');
135
+ mergeDoc(userdoc, mergedUserDocs, 'methods');
136
+ if (userdoc.notice) {
137
+ if (mergedUserDocs.notice && mergedUserDocs.notice != userdoc.notice) {
138
+ throw new Error(`UserDoc notice conflict `);
139
+ }
140
+ mergedUserDocs.notice = userdoc.notice;
141
+ }
142
+ }
143
+ }
144
+ return {
145
+ mergedABI,
146
+ added,
147
+ mergedDevDocs,
148
+ mergedUserDocs,
149
+ sigJSMap,
150
+ };
151
+ }