@rocketh/proxy 0.17.12 → 0.17.13

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/src/index.ts ADDED
@@ -0,0 +1,484 @@
1
+ import {Abi, AbiFunction} from 'abitype';
2
+ import type {Artifact, DeploymentConstruction, Deployment, Environment} from '@rocketh/core/types';
3
+ import {resolveAccount} from '@rocketh/core/account';
4
+ import type {EIP1193Account} from 'eip-1193';
5
+ import {Chain, encodeFunctionData, zeroAddress} from 'viem';
6
+ import {logs} from 'named-logs';
7
+ import {deploy, DeployOptions} from '@rocketh/deploy';
8
+ import {checkUpgradeIndex, replaceTemplateArgs} from './utils.js';
9
+ import ERC1967Proxy from './hardhat-deploy-v1-artifacts/ERC1967Proxy.js';
10
+ import ERC173Proxy from './hardhat-deploy-v1-artifacts/EIP173Proxy.js';
11
+ import ERC173ProxyWithReceive from './hardhat-deploy-v1-artifacts/EIP173ProxyWithReceive.js';
12
+ import TransparentUpgradeableProxy from './hardhat-deploy-v1-artifacts/TransparentUpgradeableProxy.js';
13
+ import OptimizedTransparentUpgradeableProxy from './hardhat-deploy-v1-artifacts/OptimizedTransparentUpgradeableProxy.js';
14
+ import DefaultProxyAdmin from './hardhat-deploy-v1-artifacts/ProxyAdmin.js';
15
+ import {execute, read} from '@rocketh/read-execute';
16
+ import {mergeABIs} from '@rocketh/core/artifacts';
17
+ import {toJSONCompatibleLinkedData} from '@rocketh/core/json';
18
+
19
+ const logger = logs('@rocketh/proxy');
20
+
21
+ export type PredefinedProxyContract =
22
+ | 'ERC173Proxy'
23
+ | 'ERC173ProxyWithReceive'
24
+ | 'UUPS'
25
+ | 'SharedAdminOpenZeppelinTransparentProxy'
26
+ | 'SharedAdminOptimizedTransparentProxy';
27
+
28
+ export type ProxyDeployOptions = Omit<DeployOptions, 'skipIfAlreadyDeployed' | 'alwaysOverride'> & {
29
+ proxyDisabled?: boolean;
30
+ owner?: EIP1193Account;
31
+ execute?:
32
+ | string
33
+ | {
34
+ methodName: string;
35
+ args: unknown[]; // TODO types
36
+ };
37
+ upgradeIndex?: number;
38
+ checkProxyAdmin?: boolean;
39
+ checkABIConflict?: boolean;
40
+ deterministicImplementation?: boolean;
41
+ proxyContract?:
42
+ | PredefinedProxyContract
43
+ | ({type: PredefinedProxyContract} & {
44
+ type: 'SharedAdminOpenZeppelinTransparentProxy' | 'SharedAdminOptimizedTransparentProxy';
45
+ proxyAdminName?: string;
46
+ // TODO allow custom proxyAdmin artifact?
47
+ })
48
+ | {
49
+ type: 'custom';
50
+ artifact: Artifact;
51
+ args?: ('{implementation}' | '{admin}' | '{data}')[]; // default to ['{implementation}', '{admin}', '{data}']
52
+ // TODO allow viaAdminContract for custom proxy artifacts
53
+ // We could just use boolean | {proxyAdminName: string}
54
+ // viaAdminContract?:
55
+ // | string
56
+ // | {
57
+ // name: string;
58
+ // artifact?: string | ArtifactData;
59
+ // };
60
+ // viaAdminContract = {
61
+ // artifactName: 'DefaultProxyAdmin',
62
+ // proxyAdminName:
63
+ // (typeof options.proxyContract === 'object' && options.proxyContract.proxyAdminName) ||
64
+ // 'DefaultProxyAdmin',
65
+ // };
66
+ };
67
+ };
68
+
69
+ export type ImplementationDeployer<TAbi extends Abi> = (
70
+ name: string,
71
+ args: Omit<DeploymentConstruction<TAbi>, 'artifact'>,
72
+ options?: DeployOptions,
73
+ ) => Promise<Deployment<TAbi>>;
74
+
75
+ // TODO omit nonce ? // TODO omit chain ? same for rocketh-deploy
76
+ export type ProxyEnhancedDeploymentConstruction<TAbi extends Abi> = Omit<DeploymentConstruction<TAbi>, 'artifact'> & {
77
+ artifact: Artifact<TAbi> | ImplementationDeployer<TAbi>;
78
+ };
79
+
80
+ export type ProxyEnhancedDeploymentConstructionWithoutFunction<TAbi extends Abi> = Omit<
81
+ DeploymentConstruction<TAbi>,
82
+ 'artifact'
83
+ > & {
84
+ artifact: Artifact<TAbi>;
85
+ };
86
+
87
+ export type DeployViaProxyFunction = <TAbi extends Abi>(
88
+ name: string,
89
+ params: ProxyEnhancedDeploymentConstruction<TAbi>,
90
+ options?: ProxyDeployOptions,
91
+ ) => Promise<Deployment<TAbi>>;
92
+
93
+ export function deployViaProxy(
94
+ env: Environment,
95
+ ): <TAbi extends Abi>(
96
+ name: string,
97
+ params: ProxyEnhancedDeploymentConstruction<TAbi>,
98
+ options?: ProxyDeployOptions,
99
+ ) => Promise<Deployment<TAbi>> {
100
+ const _deploy = deploy(env);
101
+ const _read = read(env);
102
+ const _execute = execute(env);
103
+ return async <TAbi extends Abi>(
104
+ name: string,
105
+ params: ProxyEnhancedDeploymentConstruction<TAbi>,
106
+ options?: ProxyDeployOptions,
107
+ ) => {
108
+ let optionsForImplementation = options
109
+ ? {
110
+ alwaysOverride: true,
111
+ deterministic: options.deterministic || options.deterministicImplementation,
112
+ libraries: options.libraries,
113
+ }
114
+ : undefined;
115
+ let optionsForProxy = options
116
+ ? ((options) => {
117
+ const {
118
+ owner,
119
+ checkABIConflict,
120
+ checkProxyAdmin,
121
+ execute,
122
+ deterministicImplementation,
123
+ proxyContract,
124
+ proxyDisabled,
125
+ upgradeIndex,
126
+ linkedData,
127
+ ...optionsForProxy
128
+ } = options;
129
+ return optionsForProxy;
130
+ })(options)
131
+ : undefined;
132
+
133
+ const proxyName = `${name}_Proxy`;
134
+ const implementationName = `${name}_Implementation`;
135
+
136
+ let existingDeployment = env.getOrNull<TAbi>(name);
137
+
138
+ if (options?.proxyDisabled) {
139
+ if (existingDeployment) {
140
+ throw new Error(`cannot deploy ${name} with proxyDisabled, already deployed`);
141
+ } else {
142
+ if (typeof params.artifact === 'function') {
143
+ return params.artifact(name, params, {...optionsForProxy, linkedData: options.linkedData});
144
+ } else {
145
+ // TODO any ?
146
+ return _deploy<TAbi>(name, params as any, {...optionsForProxy, linkedData: options.linkedData});
147
+ }
148
+ }
149
+ }
150
+ const deployResult = checkUpgradeIndex(existingDeployment, options?.upgradeIndex);
151
+ if (deployResult) {
152
+ return deployResult;
153
+ }
154
+
155
+ const {account, artifact, args, ...viemArgs} = params;
156
+
157
+ if (!account) {
158
+ throw new Error(`no account specified`);
159
+ }
160
+ const address = resolveAccount(account, env);
161
+
162
+ let viaAdminContract: {artifactName: 'DefaultProxyAdmin'; proxyAdminName: string} | undefined;
163
+
164
+ let proxyArgsTemplate = ['{implementation}', '{admin}', '{data}'];
165
+ let proxyArtifact: Artifact = ERC173Proxy;
166
+ let checkABIConflict = true;
167
+ let checkProxyAdmin = true;
168
+ if (options?.proxyContract) {
169
+ if (typeof options.proxyContract !== 'string' && options.proxyContract.type === 'custom') {
170
+ proxyArtifact = options.proxyContract.artifact;
171
+ proxyArgsTemplate = options.proxyContract.args || ['{implementation}', '{admin}', '{data}'];
172
+ } else {
173
+ const proxyContractDefinition =
174
+ typeof options.proxyContract === 'string' ? options.proxyContract : options.proxyContract.type;
175
+
176
+ switch (proxyContractDefinition) {
177
+ case 'ERC173Proxy':
178
+ proxyArtifact = ERC173Proxy;
179
+ proxyArgsTemplate = ['{implementation}', '{admin}', '{data}'];
180
+ break;
181
+ case 'ERC173ProxyWithReceive':
182
+ proxyArtifact = ERC173ProxyWithReceive;
183
+ proxyArgsTemplate = ['{implementation}', '{admin}', '{data}'];
184
+ break;
185
+ case 'UUPS':
186
+ checkABIConflict = false;
187
+ checkProxyAdmin = false;
188
+ proxyArtifact = ERC1967Proxy;
189
+ proxyArgsTemplate = ['{implementation}', '{data}'];
190
+ break;
191
+ case 'SharedAdminOpenZeppelinTransparentProxy':
192
+ checkABIConflict = false;
193
+ proxyArtifact = TransparentUpgradeableProxy;
194
+ proxyArgsTemplate = ['{implementation}', '{admin}', '{data}'];
195
+ viaAdminContract = {
196
+ artifactName: 'DefaultProxyAdmin',
197
+ proxyAdminName:
198
+ (typeof options.proxyContract === 'object' && options.proxyContract.proxyAdminName) ||
199
+ 'DefaultProxyAdmin',
200
+ };
201
+ break;
202
+ case 'SharedAdminOptimizedTransparentProxy':
203
+ checkABIConflict = false;
204
+ proxyArtifact = OptimizedTransparentUpgradeableProxy;
205
+ proxyArgsTemplate = ['{implementation}', '{admin}', '{data}'];
206
+ viaAdminContract = {
207
+ artifactName: 'DefaultProxyAdmin',
208
+ proxyAdminName:
209
+ (typeof options.proxyContract === 'object' && options.proxyContract.proxyAdminName) ||
210
+ 'DefaultProxyAdmin',
211
+ };
212
+ break;
213
+ default:
214
+ throw new Error(`unknown proxy contract ${options.proxyContract}`);
215
+ }
216
+ }
217
+ }
218
+
219
+ checkABIConflict = options?.checkABIConflict ?? checkABIConflict;
220
+ checkProxyAdmin = options?.checkProxyAdmin ?? checkProxyAdmin;
221
+
222
+ const implementationDeployment =
223
+ typeof params.artifact === 'function'
224
+ ? await params.artifact(implementationName, {...params}, optionsForImplementation)
225
+ : await _deploy(
226
+ implementationName,
227
+ {
228
+ ...viemArgs,
229
+ args,
230
+ artifact,
231
+ account: address,
232
+ } as DeploymentConstruction<TAbi>,
233
+ optionsForImplementation,
234
+ );
235
+
236
+ logger.info(`implementation at ${implementationDeployment.address}`, `${implementationName}`);
237
+
238
+ const {
239
+ address: implementationAddress,
240
+ argsData: implementationArgsData,
241
+ transaction,
242
+ newlyDeployed: implementationNewlyDeployed,
243
+ ...artifactFromImplementationDeployment
244
+ } = implementationDeployment;
245
+
246
+ // TODO throw specific error if artifact not found
247
+ const artifactToUse = artifactFromImplementationDeployment;
248
+ const {mergedABI} = mergeABIs(
249
+ [
250
+ {name: implementationName, abi: artifactFromImplementationDeployment.abi},
251
+ {name: proxyName, abi: proxyArtifact.abi},
252
+ ],
253
+ {doNotCheckForConflicts: !checkABIConflict},
254
+ );
255
+
256
+ logger.info(`existingDeployment at ${existingDeployment?.address}`);
257
+
258
+ const expectedOwner = options?.owner || address;
259
+ let proxyAdmin = expectedOwner;
260
+
261
+ let proxyAdminContract:
262
+ | {
263
+ deployment: Deployment<typeof DefaultProxyAdmin.abi>;
264
+ owner: `0x${string}`;
265
+ }
266
+ | undefined;
267
+ if (viaAdminContract?.artifactName === 'DefaultProxyAdmin') {
268
+ const proxyAdminOwner = expectedOwner;
269
+ const proxyAdminName = viaAdminContract.proxyAdminName;
270
+ let proxyAdminDeployed: Deployment<typeof DefaultProxyAdmin.abi> | null = env.getOrNull(proxyAdminName);
271
+
272
+ if (!proxyAdminDeployed) {
273
+ const proxyAdminDeployment = await _deploy(
274
+ proxyAdminName,
275
+ {
276
+ ...params,
277
+ artifact: DefaultProxyAdmin,
278
+ args: [proxyAdminOwner],
279
+ },
280
+ {
281
+ deterministic: options?.deterministic,
282
+ },
283
+ );
284
+ proxyAdminDeployed = proxyAdminDeployment;
285
+ }
286
+
287
+ const currentProxyAdminOwner = await _read(proxyAdminDeployed, {functionName: 'owner'});
288
+
289
+ if (currentProxyAdminOwner.toLowerCase() !== expectedOwner.toLowerCase()) {
290
+ throw new Error(`To change owner/admin, you need to call transferOwnership on ${proxyAdminName}`);
291
+ }
292
+ if (currentProxyAdminOwner === zeroAddress) {
293
+ throw new Error(`The Proxy Admin (${proxyAdminName}) belongs to no-one. The Proxy cannot be upgraded anymore`);
294
+ }
295
+ proxyAdmin = proxyAdminDeployed.address;
296
+
297
+ proxyAdminContract = {
298
+ deployment: proxyAdminDeployed,
299
+ owner: currentProxyAdminOwner.toLowerCase() as `0x${string}`,
300
+ };
301
+ }
302
+
303
+ let postUpgradeCalldata: `0x${string}` | undefined;
304
+ if (options?.execute) {
305
+ const methodName = typeof options.execute === 'string' ? options.execute : options.execute.methodName;
306
+ const argsToExecute = typeof options.execute === 'string' ? (args as unknown[]) : options.execute.args;
307
+ const method: AbiFunction | undefined = artifactToUse.abi.find(
308
+ (v) => v.type === 'function' && v.name === methodName,
309
+ ) as AbiFunction;
310
+ if (method) {
311
+ postUpgradeCalldata = encodeFunctionData({
312
+ ...viemArgs,
313
+ args: argsToExecute,
314
+ account: address,
315
+ abi: [method],
316
+ functionName: method.name,
317
+ });
318
+ } else {
319
+ throw new Error(`Method ${methodName} not found in artifact provided for ${name}`);
320
+ }
321
+ }
322
+ // let preUpgradeCalldata: `0x${string}` | undefined;
323
+ // if (options?.preExecute) {
324
+ // const method: AbiFunction | undefined = artifactToUse.abi.find(
325
+ // (v) => v.type === 'function' && v.name === options.preExecute
326
+ // ) as AbiFunction;
327
+ // if (method) {
328
+ // preUpgradeCalldata = encodeFunctionData({...viemArgs, account, abi: [method], functionName: method.name});
329
+ // }
330
+ // }
331
+
332
+ if (!existingDeployment) {
333
+ const {newlyDeployed, ...proxy} = await _deploy<typeof proxyArtifact.abi>(
334
+ proxyName,
335
+ {
336
+ ...params,
337
+ artifact: proxyArtifact,
338
+ args: replaceTemplateArgs(proxyArgsTemplate, {
339
+ implementationAddress: implementationDeployment.address,
340
+ proxyAdmin: proxyAdmin,
341
+ data: postUpgradeCalldata ? postUpgradeCalldata : '0x',
342
+ }),
343
+ },
344
+ {
345
+ skipIfAlreadyDeployed: true,
346
+ deterministic: options?.deterministic,
347
+ },
348
+ );
349
+
350
+ logger.info(`proxy deployed at ${proxy.address}`);
351
+
352
+ existingDeployment = await env.save<TAbi>(name, {
353
+ ...proxy,
354
+ ...artifactToUse,
355
+ abi: mergedABI as unknown as TAbi,
356
+ linkedData: toJSONCompatibleLinkedData(options?.linkedData),
357
+ });
358
+
359
+ logger.info(`saving as ${name}`);
360
+ } else {
361
+ const proxyDeployment = env.getOrNull<typeof proxyArtifact.abi>(proxyName);
362
+ if (!proxyDeployment) {
363
+ throw new Error(`deployment for "${name}" exits but there is no proxy`);
364
+ }
365
+
366
+ const implementationSlotData = await env.network.provider.request({
367
+ method: 'eth_getStorageAt',
368
+ params: [
369
+ proxyDeployment.address,
370
+ '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc',
371
+ 'latest',
372
+ ],
373
+ });
374
+ const currentImplementationAddress = `0x${implementationSlotData.substr(-40)}`;
375
+
376
+ if (currentImplementationAddress.toLowerCase() !== implementationDeployment.address.toLowerCase()) {
377
+ logger.info(
378
+ `different implementation old: ${currentImplementationAddress} new: ${implementationDeployment.address}, upgrade...`,
379
+ );
380
+
381
+ // let currentOwner: `0x${string}` | undefined;
382
+ // try {
383
+ // currentOwner = await env.read(proxyDeployment, {functionName: 'owner'});
384
+ // console.log({currentOwner});
385
+ // } catch {
386
+ // currentOwner = undefined;
387
+ // }
388
+ // if (!currentOwner) {
389
+ const ownerSlotData = await env.network.provider.request({
390
+ method: 'eth_getStorageAt',
391
+ params: [
392
+ proxyDeployment.address,
393
+ '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103',
394
+ 'latest',
395
+ ],
396
+ });
397
+ let currentOwner = `0x${ownerSlotData.substr(-40)}`;
398
+
399
+ if (currentOwner === zeroAddress) {
400
+ try {
401
+ const owner = await _read(existingDeployment as any, {functionName: 'owner'});
402
+ currentOwner = (owner as string).toLowerCase() as `0x${string}`;
403
+ } catch (err) {}
404
+ }
405
+
406
+ if (currentOwner === zeroAddress) {
407
+ if (checkProxyAdmin) {
408
+ throw new Error('The Proxy belongs to no-one. It cannot be upgraded anymore');
409
+ }
410
+ } else if (currentOwner.toLowerCase() !== proxyAdmin.toLowerCase()) {
411
+ throw new Error(
412
+ `To change owner/admin, you need to call the proxy directly, it currently is ${currentOwner}`,
413
+ );
414
+ }
415
+
416
+ // if (preUpgradeCalldata) {
417
+ // if (postUpgradeCalldata) {
418
+ // await env.execute(proxyDeployment, {
419
+ // account: address,
420
+ // functionName: 'callAndUpgradeToAndCall',
421
+ // args: [implementation.address, preUpgradeCalldata, postUpgradeCalldata],
422
+ // value: 0n, // TODO
423
+ // });
424
+ // } else {
425
+ // await env.execute(proxyDeployment, {
426
+ // account: address,
427
+ // functionName: 'callAndUpgradeToAndCall',
428
+ // args: [implementation.address, preUpgradeCalldata, '0x'],
429
+ // value: 0n, // TODO
430
+ // });
431
+ // }
432
+ // } else
433
+
434
+ const deploymentToUseForUpgrade = options?.proxyContract === 'UUPS' ? existingDeployment : proxyDeployment;
435
+
436
+ let useUpgradeToAndCall = !!postUpgradeCalldata;
437
+ if (!useUpgradeToAndCall) {
438
+ if (!deploymentToUseForUpgrade.abi.find((v) => v.type === 'function' && v.name === 'upgradeTo')) {
439
+ useUpgradeToAndCall = true;
440
+ }
441
+ }
442
+
443
+ if (proxyAdminContract) {
444
+ if (useUpgradeToAndCall) {
445
+ await _execute(proxyAdminContract.deployment, {
446
+ account: proxyAdminContract.owner,
447
+ functionName: 'upgradeAndCall',
448
+ args: [proxyDeployment.address, implementationDeployment.address, postUpgradeCalldata || '0x'],
449
+ value: 0n, // TODO
450
+ });
451
+ } else {
452
+ await _execute(proxyAdminContract.deployment, {
453
+ account: proxyAdminContract.owner,
454
+ functionName: 'upgrade',
455
+ args: [proxyDeployment.address, implementationDeployment.address],
456
+ });
457
+ }
458
+ } else {
459
+ if (useUpgradeToAndCall) {
460
+ await _execute(deploymentToUseForUpgrade, {
461
+ account: currentOwner,
462
+ functionName: 'upgradeToAndCall',
463
+ args: [implementationDeployment.address, postUpgradeCalldata || '0x'],
464
+ value: 0n, // TODO
465
+ });
466
+ } else {
467
+ await _execute(deploymentToUseForUpgrade, {
468
+ account: currentOwner,
469
+ functionName: 'upgradeTo',
470
+ args: [implementationDeployment.address],
471
+ });
472
+ }
473
+ }
474
+ }
475
+ existingDeployment = await env.save(name, {
476
+ ...proxyDeployment,
477
+ ...artifactToUse,
478
+ linkedData: toJSONCompatibleLinkedData(options?.linkedData),
479
+ });
480
+ logger.info(`saving as ${name}`);
481
+ }
482
+ return existingDeployment;
483
+ };
484
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,89 @@
1
+ import {DeployResult} from '@rocketh/deploy';
2
+ import type {Abi, Deployment} from '@rocketh/core/types';
3
+
4
+ export function checkUpgradeIndex<TAbi extends Abi>(
5
+ oldDeployment: Deployment<TAbi> | null,
6
+ upgradeIndex?: number,
7
+ ): DeployResult<TAbi> | undefined {
8
+ if (typeof upgradeIndex === 'undefined') {
9
+ return;
10
+ }
11
+ if (upgradeIndex === 0) {
12
+ if (oldDeployment) {
13
+ return {...oldDeployment, newlyDeployed: false};
14
+ }
15
+ } else if (upgradeIndex === 1) {
16
+ if (!oldDeployment) {
17
+ throw new Error('upgradeIndex === 1 : expects Deployments to already exists');
18
+ }
19
+ const history: any[] | undefined = oldDeployment.history as any[] | undefined;
20
+ const numDeployments: number | undefined = oldDeployment.numDeployments as number | undefined;
21
+ if ((history && history.length > 0) || (numDeployments && numDeployments > 1)) {
22
+ return {...oldDeployment, newlyDeployed: false};
23
+ }
24
+ } else {
25
+ if (!oldDeployment) {
26
+ throw new Error(`upgradeIndex === ${upgradeIndex} : expects Deployments to already exists`);
27
+ }
28
+
29
+ const history: any[] | undefined = oldDeployment.history as any[] | undefined;
30
+ const numDeployments: number | undefined = oldDeployment.numDeployments as number | undefined;
31
+ if (!history) {
32
+ if (numDeployments && numDeployments > 1) {
33
+ if (numDeployments > upgradeIndex) {
34
+ return {...oldDeployment, newlyDeployed: false};
35
+ } else if (numDeployments < upgradeIndex) {
36
+ throw new Error(
37
+ `upgradeIndex === ${upgradeIndex} : expects Deployments numDeployments to be at least ${upgradeIndex}`,
38
+ );
39
+ }
40
+ } else {
41
+ throw new Error(
42
+ `upgradeIndex > 1 : expects Deployments history to exists, or numDeployments to be greater than 1`,
43
+ );
44
+ }
45
+ } else if (history.length > upgradeIndex - 1) {
46
+ return {...oldDeployment, newlyDeployed: false};
47
+ } else if (history.length < upgradeIndex - 1) {
48
+ throw new Error(
49
+ `upgradeIndex === ${upgradeIndex} : expects Deployments history length to be at least ${upgradeIndex - 1}`,
50
+ );
51
+ }
52
+ }
53
+ }
54
+
55
+ export function replaceTemplateArgs(
56
+ proxyArgsTemplate: string[],
57
+ {
58
+ implementationAddress,
59
+ proxyAdmin,
60
+ data,
61
+ proxyAddress,
62
+ }: {
63
+ implementationAddress: string;
64
+ proxyAdmin: string;
65
+ data: string;
66
+ proxyAddress?: string;
67
+ },
68
+ ): any[] {
69
+ const proxyArgs: any[] = [];
70
+ for (let i = 0; i < proxyArgsTemplate.length; i++) {
71
+ const argValue = proxyArgsTemplate[i];
72
+ if (argValue === '{implementation}') {
73
+ proxyArgs.push(implementationAddress);
74
+ } else if (argValue === '{admin}') {
75
+ proxyArgs.push(proxyAdmin);
76
+ } else if (argValue === '{data}') {
77
+ proxyArgs.push(data);
78
+ } else if (argValue === '{proxy}') {
79
+ if (!proxyAddress) {
80
+ throw new Error(`Expected proxy address but none was specified.`);
81
+ }
82
+ proxyArgs.push(proxyAddress);
83
+ } else {
84
+ proxyArgs.push(argValue);
85
+ }
86
+ }
87
+
88
+ return proxyArgs;
89
+ }