rocketh 0.11.20 → 0.11.22-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.
Files changed (43) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/environment/index.d.ts +0 -3
  4. package/dist/environment/index.d.ts.map +1 -1
  5. package/dist/environment/index.js +14 -15
  6. package/dist/environment/index.js.map +1 -1
  7. package/dist/environment/types.d.ts +8 -4
  8. package/dist/environment/types.d.ts.map +1 -1
  9. package/dist/executor/index.d.ts +98 -3
  10. package/dist/executor/index.d.ts.map +1 -1
  11. package/dist/executor/index.js +82 -36
  12. package/dist/executor/index.js.map +1 -1
  13. package/dist/executor/setup.test.d.ts +13 -0
  14. package/dist/executor/setup.test.d.ts.map +1 -0
  15. package/dist/executor/setup.test.js +106 -0
  16. package/dist/executor/setup.test.js.map +1 -0
  17. package/dist/executor/types.d.ts +20 -0
  18. package/dist/executor/types.d.ts.map +1 -1
  19. package/dist/index.d.ts +0 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +0 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/internal/types.d.ts +2 -0
  24. package/dist/internal/types.d.ts.map +1 -1
  25. package/dist/utils/curry.d.ts +35 -0
  26. package/dist/utils/curry.d.ts.map +1 -0
  27. package/dist/utils/curry.js +30 -0
  28. package/dist/utils/curry.js.map +1 -0
  29. package/dist/utils/curry.test.d.ts +16 -0
  30. package/dist/utils/curry.test.d.ts.map +1 -0
  31. package/dist/utils/curry.test.js +33 -0
  32. package/dist/utils/curry.test.js.map +1 -0
  33. package/package.json +1 -1
  34. package/src/cli.ts +1 -1
  35. package/src/environment/index.ts +16 -18
  36. package/src/environment/types.ts +10 -4
  37. package/src/executor/index.ts +111 -53
  38. package/src/executor/setup.test.ts +133 -0
  39. package/src/executor/types.ts +45 -0
  40. package/src/index.ts +0 -1
  41. package/src/internal/types.ts +3 -0
  42. package/src/utils/curry.test.ts +42 -0
  43. package/src/utils/curry.ts +60 -0
@@ -5,13 +5,13 @@ import type {
5
5
  Config,
6
6
  Environment,
7
7
  ResolvedConfig,
8
- ResolvedNamedAccounts,
9
8
  UnknownDeployments,
10
9
  UnresolvedNetworkSpecificData,
11
10
  UnresolvedUnknownNamedAccounts,
12
11
  } from '../environment/types.js';
13
- import {createEnvironment} from '../environment/index.js';
14
- import {DeployScriptFunction, DeployScriptModule} from './types.js';
12
+ import {createEnvironment, SignerProtocolFunction} from '../environment/index.js';
13
+ import {DeployScriptFunction, DeployScriptModule, EnhancedDeployScriptFunction, EnhancedEnvironment} from './types.js';
14
+ import {withEnvironment} from '../utils/curry.js';
15
15
  import {logger, setLogLevel, spin} from '../internal/logging.js';
16
16
  import {EIP1193GenericRequestProvider, EIP1193ProviderWithoutEvents} from 'eip-1193';
17
17
  import {getRoughGasPriceEstimate} from '../utils/eth.js';
@@ -19,24 +19,70 @@ import prompts from 'prompts';
19
19
  import {formatEther} from 'viem';
20
20
  import {tsImport} from 'tsx/esm/api';
21
21
 
22
- export function execute<
22
+ /**
23
+ * Setup function that creates the execute function for deploy scripts. It allow to specify a set of functions that will be available in the environment.
24
+ *
25
+ * @param functions - An object of utility functions that expect Environment as their first parameter
26
+ * @returns An execute function that provides an enhanced environment with curried functions
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const functions = {
31
+ * deploy: (env: Environment, contractName: string, args: any[]) => Promise<void>,
32
+ * verify: (env: Environment, address: string) => Promise<boolean>
33
+ * };
34
+ *
35
+ * const execute = setup(functions);
36
+ *
37
+ * export default execute(async (env, args) => {
38
+ * // env now includes both the original environment AND the curried functions
39
+ * await env.deploy('MyContract', []); // No need to pass env
40
+ * await env.verify('0x123...'); // No need to pass env
41
+ *
42
+ * // Original environment properties are still available
43
+ * console.log(env.network.name);
44
+ * const deployment = env.get('MyContract');
45
+ * }, { tags: ['deploy'] });
46
+ * ```
47
+ */
48
+ export function setup<
49
+ Functions extends Record<string, (env: Environment<any, any, any>, ...args: any[]) => any> = {},
23
50
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
24
51
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
25
- ArgumentsType = undefined,
26
52
  Deployments extends UnknownDeployments = UnknownDeployments
27
- >(
28
- callback: DeployScriptFunction<NamedAccounts, Data, ArgumentsType, Deployments>,
29
- options: {tags?: string[]; dependencies?: string[]; id?: string}
30
- ): DeployScriptModule<NamedAccounts, Data, ArgumentsType, Deployments> {
31
- const scriptModule: DeployScriptModule<NamedAccounts, Data, ArgumentsType, Deployments> = (
32
- env: Environment<NamedAccounts, Data, Deployments>,
33
- args?: ArgumentsType
34
- ) => callback(env, args);
35
- scriptModule.tags = options.tags;
36
- scriptModule.dependencies = options.dependencies;
37
- scriptModule.id = options.id;
38
- // TODO runAtTheEnd ?
39
- return scriptModule;
53
+ >(functions: Functions, signerProtocols?: Record<string, SignerProtocolFunction>) {
54
+ return function enhancedExecute<ArgumentsType = undefined>(
55
+ callback: EnhancedDeployScriptFunction<NamedAccounts, Data, ArgumentsType, Deployments, Functions>,
56
+ options: {tags?: string[]; dependencies?: string[]; id?: string; runAtTheEnd?: boolean}
57
+ ): DeployScriptModule<NamedAccounts, Data, ArgumentsType, Deployments> {
58
+ const scriptModule: DeployScriptModule<NamedAccounts, Data, ArgumentsType, Deployments> = (
59
+ env: Environment<NamedAccounts, Data, Deployments>,
60
+ args?: ArgumentsType
61
+ ) => {
62
+ if (signerProtocols) {
63
+ for (const key of Object.keys(signerProtocols)) {
64
+ env.registerProtocol(key, signerProtocols[key]);
65
+ }
66
+ }
67
+
68
+ // Create the enhanced environment by combining the original environment with curried functions
69
+ const curriedFunctions = withEnvironment(env, functions);
70
+ const enhancedEnv = Object.assign(
71
+ Object.create(Object.getPrototypeOf(env)),
72
+ env,
73
+ curriedFunctions
74
+ ) as EnhancedEnvironment<NamedAccounts, Data, Deployments, Functions>;
75
+
76
+ return callback(enhancedEnv, args);
77
+ };
78
+
79
+ scriptModule.tags = options.tags;
80
+ scriptModule.dependencies = options.dependencies;
81
+ scriptModule.id = options.id;
82
+ scriptModule.runAtTheEnd = options.runAtTheEnd;
83
+
84
+ return scriptModule;
85
+ };
40
86
  }
41
87
 
42
88
  export type NamedAccountExecuteFunction<
@@ -107,43 +153,10 @@ export type UserConfig<
107
153
  data?: Data;
108
154
  };
109
155
 
110
- export async function readConfig<
156
+ export function transformUserConfig<
111
157
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
112
158
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData
113
- >(options: ConfigOptions): Promise<Config<NamedAccounts, Data>> {
114
- type ConfigFile = UserConfig<NamedAccounts, Data>;
115
- let configFile: ConfigFile | undefined;
116
-
117
- // TODO more sophisticated config file finding mechanism (look up parents, etc..)
118
- const tsFilePath = path.join(process.cwd(), 'rocketh.ts');
119
- const jsFilePath = path.join(process.cwd(), 'rocketh.js');
120
-
121
- const tsVersionExists = fs.existsSync(tsFilePath);
122
- const jsVersionExists = fs.existsSync(jsFilePath);
123
- const existingConfigs = [tsVersionExists, jsVersionExists].filter(Boolean).length;
124
-
125
- // console.log({tsFilePath, tsVersionExists, existingConfigs});
126
-
127
- // Throw error if multiple config files exist
128
- if (existingConfigs > 1) {
129
- throw new Error('Multiple configuration files found. Please use only one of: rocketh.ts, rocketh.js');
130
- }
131
- if (tsVersionExists) {
132
- const moduleLoaded = await tsImport(`file://${tsFilePath}`, import.meta.url);
133
- configFile = moduleLoaded.config;
134
- // console.log({tsVersionExists: configFile});
135
- // if ((configFile as any).default) {
136
- // configFile = (configFile as any).default as ConfigFile;
137
- // if ((configFile as any).default) {
138
- // logger.warn(`double default...`);
139
- // configFile = (configFile as any).default as ConfigFile;
140
- // }
141
- // }
142
- } else if (jsVersionExists) {
143
- const moduleLoaded = await tsImport(`file://${jsFilePath}`, import.meta.url);
144
- configFile = moduleLoaded.config;
145
- }
146
-
159
+ >(configFile: UserConfig<NamedAccounts, Data> | undefined, options: ConfigOptions) {
147
160
  if (configFile) {
148
161
  if (!options.deployments && configFile.deployments) {
149
162
  options.deployments = configFile.deployments;
@@ -257,6 +270,51 @@ export async function readConfig<
257
270
  }
258
271
  }
259
272
 
273
+ export async function readConfig<
274
+ NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
275
+ Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData
276
+ >(options: ConfigOptions): Promise<Config<NamedAccounts, Data>> {
277
+ type ConfigFile = UserConfig<NamedAccounts, Data>;
278
+ let configFile: ConfigFile | undefined;
279
+
280
+ let tsVersion: string | undefined;
281
+ let jsVersion: string | undefined;
282
+
283
+ if (typeof process !== 'undefined') {
284
+ // TODO more sophisticated config file finding mechanism (look up parents, etc..)
285
+ const tsFilePath = path.join(process.cwd(), 'rocketh.ts');
286
+ const jsFilePath = path.join(process.cwd(), 'rocketh.js');
287
+
288
+ tsVersion = fs.existsSync(tsFilePath) ? `file://${tsFilePath}` : undefined;
289
+ jsVersion = fs.existsSync(jsFilePath) ? `file://${jsFilePath}` : undefined;
290
+ }
291
+ const existingConfigs = [tsVersion, jsVersion].filter(Boolean).length;
292
+
293
+ // console.log({tsFilePath, tsVersionExists, existingConfigs});
294
+
295
+ // Throw error if multiple config files exist
296
+ if (existingConfigs > 1) {
297
+ throw new Error('Multiple configuration files found. Please use only one of: rocketh.ts, rocketh.js');
298
+ }
299
+ if (tsVersion) {
300
+ const moduleLoaded = await tsImport(tsVersion, import.meta.url);
301
+ configFile = moduleLoaded.config;
302
+ // console.log({tsVersionExists: configFile});
303
+ // if ((configFile as any).default) {
304
+ // configFile = (configFile as any).default as ConfigFile;
305
+ // if ((configFile as any).default) {
306
+ // logger.warn(`double default...`);
307
+ // configFile = (configFile as any).default as ConfigFile;
308
+ // }
309
+ // }
310
+ } else if (jsVersion) {
311
+ const moduleLoaded = await tsImport(jsVersion, import.meta.url);
312
+ configFile = moduleLoaded.config;
313
+ }
314
+
315
+ return transformUserConfig(configFile, options);
316
+ }
317
+
260
318
  export async function readAndResolveConfig<
261
319
  NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
262
320
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData
@@ -0,0 +1,133 @@
1
+ import type {Environment} from '../environment/types.js';
2
+ import {setup} from './index.js';
3
+
4
+ // Mock environment for testing
5
+ const mockEnv = {} as Environment;
6
+
7
+ // Example utility functions that take Environment as first parameter
8
+ const utilityFunctions = {
9
+ deployContract: async (env: Environment, contractName: string, args: any[] = []): Promise<string> => {
10
+ console.log(`Deploying ${contractName} with args:`, args);
11
+ return '0x1234567890123456789012345678901234567890';
12
+ },
13
+
14
+ verifyContract: async (env: Environment, address: string): Promise<boolean> => {
15
+ console.log(`Verifying contract at ${address}`);
16
+ return true;
17
+ },
18
+
19
+ getBalance: async (env: Environment, address: string): Promise<bigint> => {
20
+ console.log(`Getting balance for ${address}`);
21
+ return BigInt('1000000000000000000'); // 1 ETH
22
+ },
23
+
24
+ calculateGas: (env: Environment, operation: string): number => {
25
+ console.log(`Calculating gas for ${operation}`);
26
+ return 21000;
27
+ },
28
+
29
+ isDeployed: (env: Environment, contractName: string): boolean => {
30
+ console.log(`Checking if ${contractName} is deployed`);
31
+ return false;
32
+ },
33
+ };
34
+
35
+ // Create the enhanced execute function using setup
36
+ const execute = setup(utilityFunctions);
37
+
38
+ // Test the enhanced execute function
39
+ const testScript = execute(
40
+ async (env, args) => {
41
+ // Type test: env should have both original Environment properties AND curried functions
42
+
43
+ // Test curried functions (no need to pass env)
44
+ const address = await env.deployContract('MyToken', ['TokenName', 'TKN']);
45
+ const isVerified = await env.verifyContract(address);
46
+ const balance = await env.getBalance(address);
47
+ const gasEstimate = env.calculateGas('transfer');
48
+ const deployed = env.isDeployed('MyToken');
49
+
50
+ // Test that original environment properties are still accessible
51
+ // (These would normally be available on a real environment)
52
+ // console.log('Network name:', env.network?.name);
53
+ // console.log('Chain ID:', env.network?.chain?.id);
54
+
55
+ console.log('Test results:', {
56
+ address,
57
+ isVerified,
58
+ balance: balance.toString(),
59
+ gasEstimate,
60
+ deployed,
61
+ });
62
+
63
+ return true; // Return true to indicate successful completion
64
+ },
65
+ {
66
+ tags: ['test'],
67
+ dependencies: [],
68
+ id: 'test-setup-function',
69
+ }
70
+ );
71
+
72
+ // Type tests - these should compile without errors
73
+ async function testTypes() {
74
+ // The script should be a valid DeployScriptModule
75
+ console.log('Script tags:', testScript.tags);
76
+ console.log('Script dependencies:', testScript.dependencies);
77
+ console.log('Script ID:', testScript.id);
78
+
79
+ // The script should be callable with an environment
80
+ try {
81
+ const result = await testScript(mockEnv, undefined);
82
+ console.log('Script execution result:', result);
83
+ } catch (error) {
84
+ console.log('Script execution test completed (expected with mock env)');
85
+ }
86
+ }
87
+
88
+ // Example of how this would be used in a real deployment script
89
+ export const exampleUsage = () => {
90
+ // Define your utility functions
91
+ const myFunctions = {
92
+ deployERC20: async (env: Environment, name: string, symbol: string): Promise<string> => {
93
+ // Implementation would use env.save, env.network.provider, etc.
94
+ return '0x...';
95
+ },
96
+
97
+ setupPermissions: async (env: Environment, contractAddress: string, admin: string): Promise<void> => {
98
+ // Implementation would interact with contracts
99
+ },
100
+
101
+ verifyOnEtherscan: async (env: Environment, address: string, constructorArgs: any[]): Promise<boolean> => {
102
+ // Implementation would call verification service
103
+ return true;
104
+ },
105
+ };
106
+
107
+ // Create the enhanced execute function
108
+ const execute = setup(myFunctions);
109
+
110
+ // Export your deployment script
111
+ return execute(
112
+ async (env, args) => {
113
+ // Now you can use the functions without passing env each time
114
+ const tokenAddress = await env.deployERC20('MyToken', 'MTK');
115
+ await env.setupPermissions(tokenAddress, env.namedAccounts.deployer);
116
+ await env.verifyOnEtherscan(tokenAddress, ['MyToken', 'MTK']);
117
+
118
+ // Original environment is still fully accessible
119
+ console.log(`Deployed on network: ${env.network.name}`);
120
+ const deployment = env.get('MyToken');
121
+
122
+ return true;
123
+ },
124
+ {
125
+ tags: ['token', 'deploy'],
126
+ dependencies: ['setup'],
127
+ id: 'deploy-my-token',
128
+ }
129
+ );
130
+ };
131
+
132
+ // Export for potential use in actual tests
133
+ export {testTypes, testScript, utilityFunctions};
@@ -30,3 +30,48 @@ export type ScriptCallback<
30
30
  Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
31
31
  Deployments extends UnknownDeployments = UnknownDeployments
32
32
  > = (env: Environment<NamedAccounts, Data, Deployments>) => Promise<void>;
33
+
34
+ /**
35
+ * Utility type to remove the first parameter from a function type
36
+ */
37
+ type RemoveFirstParameter<T> = T extends (first: any, ...rest: infer R) => infer Return
38
+ ? (...args: R) => Return
39
+ : never;
40
+
41
+ /**
42
+ * Utility type to transform an object of functions by removing their first parameter
43
+ */
44
+ type CurriedFunctions<T> = {
45
+ [K in keyof T]: RemoveFirstParameter<T[K]>;
46
+ };
47
+
48
+ /**
49
+ * Type for the enhanced environment proxy that includes both the original environment
50
+ * and the curried functions
51
+ */
52
+ export type EnhancedEnvironment<
53
+ NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
54
+ Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
55
+ Deployments extends UnknownDeployments = UnknownDeployments,
56
+ Functions extends Record<
57
+ string,
58
+ (env: Environment<NamedAccounts, Data, Deployments>, ...args: any[]) => any
59
+ > = Record<string, (env: Environment<NamedAccounts, Data, Deployments>, ...args: any[]) => any>
60
+ > = Environment<NamedAccounts, Data, Deployments> & CurriedFunctions<Functions>;
61
+
62
+ /**
63
+ * Type for a deploy script function that receives an enhanced environment
64
+ */
65
+ export type EnhancedDeployScriptFunction<
66
+ NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
67
+ Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
68
+ ArgumentsTypes = undefined,
69
+ Deployments extends UnknownDeployments = UnknownDeployments,
70
+ Functions extends Record<
71
+ string,
72
+ (env: Environment<NamedAccounts, Data, Deployments>, ...args: any[]) => any
73
+ > = Record<string, (env: Environment<NamedAccounts, Data, Deployments>, ...args: any[]) => any>
74
+ > = (
75
+ env: EnhancedEnvironment<NamedAccounts, Data, Deployments, Functions>,
76
+ args?: ArgumentsTypes
77
+ ) => Promise<void | boolean>;
package/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  export * from './executor/index.js';
2
2
  export * from './executor/types.js';
3
3
  export * from './environment/types.js';
4
- export {extendEnvironment, handleSignerProtocol} from './environment/index.js';
5
4
  export {loadDeployments} from './environment/deployments.js';
6
5
  export * from './environment/utils/artifacts.js';
7
6
  export * from './environment/utils/chains.js';
@@ -1,5 +1,8 @@
1
+ import {SignerProtocol} from '../environment/index.js';
2
+
1
3
  export type InternalEnvironment = {
2
4
  exportDeploymentsAsTypes(): Promise<void>;
3
5
  recoverTransactionsIfAny(): Promise<void>;
4
6
  recordMigration(id: string): void;
7
+ getSignerProtocol(protocol: string): SignerProtocol | undefined;
5
8
  };
@@ -0,0 +1,42 @@
1
+ import type {Environment} from '../environment/types.js';
2
+ import {withEnvironment} from './curry.js';
3
+
4
+ // Mock environment for testing
5
+ const mockEnv = {} as Environment;
6
+
7
+ // Example functions that take environment as first parameter
8
+ const exampleFunctions = {
9
+ deploy: (env: Environment, contractName: string, args: any[]): Promise<void> => {
10
+ return Promise.resolve();
11
+ },
12
+
13
+ verify: (env: Environment, address: string): Promise<boolean> => {
14
+ return Promise.resolve(true);
15
+ },
16
+
17
+ getBalance: (env: Environment, address: string): Promise<bigint> => {
18
+ return Promise.resolve(BigInt(0));
19
+ },
20
+
21
+ syncFunction: (env: Environment, value: number): number => {
22
+ return value * 2;
23
+ },
24
+ };
25
+
26
+ // Test the currying function
27
+ const curriedFunctions = withEnvironment(mockEnv, exampleFunctions);
28
+
29
+ // Type tests - these should compile without errors
30
+ async function testTypes() {
31
+ // These calls should work without passing env
32
+ await curriedFunctions.deploy('MyContract', []);
33
+ const isVerified = await curriedFunctions.verify('0x123...');
34
+ const balance = await curriedFunctions.getBalance('0x456...');
35
+ const doubled = curriedFunctions.syncFunction(42);
36
+
37
+ console.log('Type tests passed!');
38
+ console.log({isVerified, balance, doubled});
39
+ }
40
+
41
+ // Export for potential use in actual tests
42
+ export {testTypes, curriedFunctions, exampleFunctions};
@@ -0,0 +1,60 @@
1
+ import type {
2
+ Environment,
3
+ UnknownDeployments,
4
+ UnresolvedNetworkSpecificData,
5
+ UnresolvedUnknownNamedAccounts,
6
+ } from '../environment/types.js';
7
+
8
+ /**
9
+ * Utility type to remove the first parameter from a function type
10
+ */
11
+ type RemoveFirstParameter<T> = T extends (first: any, ...rest: infer R) => infer Return
12
+ ? (...args: R) => Return
13
+ : never;
14
+
15
+ /**
16
+ * Utility type to transform an object of functions by removing their first parameter
17
+ */
18
+ type CurriedFunctions<T> = {
19
+ [K in keyof T]: RemoveFirstParameter<T[K]>;
20
+ };
21
+
22
+ /**
23
+ * Creates a curried version of functions that automatically inject an environment as the first parameter.
24
+ *
25
+ * @param env - The environment object to inject as the first parameter
26
+ * @param functions - An object containing functions that expect the environment as their first parameter
27
+ * @returns An object with the same function names, but with the environment parameter removed
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const functions = {
32
+ * deploy: (env: Environment, contractName: string, args: any[]) => Promise<void>,
33
+ * verify: (env: Environment, address: string) => Promise<boolean>
34
+ * };
35
+ *
36
+ * const curriedFunctions = withEnvironment(env, functions);
37
+ *
38
+ * // Now you can call without passing env:
39
+ * await curriedFunctions.deploy('MyContract', []);
40
+ * await curriedFunctions.verify('0x123...');
41
+ * ```
42
+ */
43
+ export function withEnvironment<
44
+ NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
45
+ Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
46
+ Deployments extends UnknownDeployments = UnknownDeployments,
47
+ T extends Record<string, (env: Environment<NamedAccounts, Data, Deployments>, ...args: any[]) => any> = Record<
48
+ string,
49
+ (env: Environment<NamedAccounts, Data, Deployments>, ...args: any[]) => any
50
+ >
51
+ >(env: Environment<NamedAccounts, Data, Deployments>, functions: T): CurriedFunctions<T> {
52
+ const result = {} as CurriedFunctions<T>;
53
+
54
+ for (const [key, func] of Object.entries(functions)) {
55
+ // Create a new function that automatically passes the environment as the first argument
56
+ (result as any)[key] = (...args: any[]) => func(env, ...args);
57
+ }
58
+
59
+ return result;
60
+ }