@openzeppelin/wizard 0.7.1 → 0.8.1

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 (41) hide show
  1. package/README.md +1 -1
  2. package/dist/account.d.ts.map +1 -1
  3. package/dist/account.js +48 -28
  4. package/dist/account.js.map +1 -1
  5. package/dist/environments/hardhat/package-lock.json +463 -521
  6. package/dist/environments/hardhat/upgradeable/package-lock.json +1282 -1055
  7. package/dist/generate/account.d.ts.map +1 -1
  8. package/dist/generate/account.js +2 -1
  9. package/dist/generate/account.js.map +1 -1
  10. package/dist/options.d.ts +3 -0
  11. package/dist/options.d.ts.map +1 -1
  12. package/dist/options.js +8 -7
  13. package/dist/options.js.map +1 -1
  14. package/dist/print.d.ts.map +1 -1
  15. package/dist/print.js +11 -11
  16. package/dist/print.js.map +1 -1
  17. package/dist/set-upgradeable.d.ts +1 -0
  18. package/dist/set-upgradeable.d.ts.map +1 -1
  19. package/dist/set-upgradeable.js +6 -0
  20. package/dist/set-upgradeable.js.map +1 -1
  21. package/dist/signer.d.ts +7 -2
  22. package/dist/signer.d.ts.map +1 -1
  23. package/dist/signer.js +45 -65
  24. package/dist/signer.js.map +1 -1
  25. package/dist/zip-foundry.d.ts.map +1 -1
  26. package/dist/zip-foundry.js +60 -65
  27. package/dist/zip-foundry.js.map +1 -1
  28. package/dist/zip-hardhat.d.ts.map +1 -1
  29. package/dist/zip-hardhat.js +8 -3
  30. package/dist/zip-hardhat.js.map +1 -1
  31. package/package.json +2 -2
  32. package/src/account.ts +55 -29
  33. package/src/environments/hardhat/package-lock.json +463 -521
  34. package/src/environments/hardhat/upgradeable/package-lock.json +1282 -1055
  35. package/src/generate/account.ts +2 -1
  36. package/src/options.ts +8 -8
  37. package/src/print.ts +31 -13
  38. package/src/set-upgradeable.ts +6 -0
  39. package/src/signer.ts +49 -70
  40. package/src/zip-foundry.ts +84 -64
  41. package/src/zip-hardhat.ts +9 -3
@@ -1,5 +1,6 @@
1
1
  import type { AccountOptions } from '../account';
2
2
  import { infoOptions } from '../set-info';
3
+ import { upgradeableOptions } from '../set-upgradeable';
3
4
  import { generateAlternatives } from './alternatives';
4
5
 
5
6
  const account = {
@@ -11,7 +12,7 @@ const account = {
11
12
  batchedExecution: [false, true] as const,
12
13
  ERC7579Modules: [false, 'AccountERC7579', 'AccountERC7579Hooked'] as const,
13
14
  access: [false] as const,
14
- upgradeable: [false] as const,
15
+ upgradeable: upgradeableOptions,
15
16
  info: infoOptions,
16
17
  };
17
18
 
package/src/options.ts CHANGED
@@ -3,15 +3,15 @@ import path from 'path';
3
3
  import type { Contract, ReferencedContract, ImportContract } from './contract';
4
4
  import { inferTranspiled } from './infer-transpiled';
5
5
 
6
- const upgradeableName = (n: string) => {
6
+ export function upgradeableName(n: string) {
7
7
  if (n === 'Initializable') {
8
8
  return n;
9
9
  } else {
10
10
  return n.replace(/(Upgradeable)?(?=\.|$)/, 'Upgradeable');
11
11
  }
12
- };
12
+ }
13
13
 
14
- const upgradeableImport = (p: ImportContract): ImportContract => {
14
+ export function upgradeableImport(p: ImportContract): ImportContract {
15
15
  const { dir, ext, name } = path.parse(p.path);
16
16
  // Use path.posix to get forward slashes
17
17
  return {
@@ -23,7 +23,7 @@ const upgradeableImport = (p: ImportContract): ImportContract => {
23
23
  name: upgradeableName(name), // Solidity file name
24
24
  }),
25
25
  };
26
- };
26
+ }
27
27
 
28
28
  export interface Options {
29
29
  transformImport?: (parent: ImportContract) => ImportContract;
@@ -32,16 +32,16 @@ export interface Options {
32
32
  export interface Helpers extends Required<Options> {
33
33
  upgradeable: boolean;
34
34
  transformName: (name: ReferencedContract) => string;
35
+ transformImport: (name: ImportContract) => ImportContract;
35
36
  }
36
37
 
37
38
  export function withHelpers(contract: Contract, opts: Options = {}): Helpers {
38
39
  const contractUpgradeable = contract.upgradeable;
39
- const transformName = (n: ReferencedContract) =>
40
- contractUpgradeable && inferTranspiled(n) ? upgradeableName(n.name) : n.name;
41
40
  return {
42
41
  upgradeable: contractUpgradeable,
43
- transformName,
44
- transformImport: p1 => {
42
+ transformName: (n: ReferencedContract) =>
43
+ contractUpgradeable && inferTranspiled(n) ? upgradeableName(n.name) : n.name,
44
+ transformImport: (p1: ImportContract) => {
45
45
  const p2 = contractUpgradeable && inferTranspiled(p1) ? upgradeableImport(p1) : p1;
46
46
  return opts.transformImport?.(p2) ?? p2;
47
47
  },
package/src/print.ts CHANGED
@@ -82,21 +82,39 @@ function printConstructor(contract: Contract, helpers: Helpers): Lines[] {
82
82
  const hasConstructorCode = contract.constructorCode.length > 0;
83
83
  const parentsWithInitializers = contract.parents.filter(hasInitializer);
84
84
  if (hasParentParams || hasConstructorCode || (helpers.upgradeable && parentsWithInitializers.length > 0)) {
85
- const parents = parentsWithInitializers.flatMap(p => printParentConstructor(p, helpers));
86
- const modifiers = helpers.upgradeable ? ['public initializer'] : parents;
87
- const args = contract.constructorArgs.map(a => printArgument(a, helpers));
88
- const body = helpers.upgradeable
89
- ? spaceBetween(
90
- parents.map(p => p + ';'),
85
+ if (helpers.upgradeable) {
86
+ const upgradeableParents = parentsWithInitializers.filter(p => inferTranspiled(p.contract));
87
+ const nonUpgradeableParents = contract.parents.filter(p => !inferTranspiled(p.contract));
88
+ const constructor = printFunction2(
89
+ [
90
+ nonUpgradeableParents.length > 0
91
+ ? '/// @custom:oz-upgrades-unsafe-allow-reachable constructor'
92
+ : '/// @custom:oz-upgrades-unsafe-allow constructor',
93
+ ],
94
+ 'constructor',
95
+ [],
96
+ nonUpgradeableParents.flatMap(p => printParentConstructor(p, helpers)),
97
+ ['_disableInitializers();'],
98
+ );
99
+ const initializer = printFunction2(
100
+ [],
101
+ 'function initialize',
102
+ contract.constructorArgs.map(a => printArgument(a, helpers)),
103
+ ['public', 'initializer'],
104
+ spaceBetween(
105
+ upgradeableParents.flatMap(p => printParentConstructor(p, helpers)).map(p => p + ';'),
91
106
  contract.constructorCode,
92
- )
93
- : contract.constructorCode;
94
- const head = helpers.upgradeable ? 'function initialize' : 'constructor';
95
- const constructor = printFunction2([], head, args, modifiers, body);
96
- if (!helpers.upgradeable) {
97
- return constructor;
107
+ ),
108
+ );
109
+ return spaceBetween(constructor, upgradeableParents.length > 0 ? initializer : []);
98
110
  } else {
99
- return spaceBetween(DISABLE_INITIALIZERS, constructor);
111
+ return printFunction2(
112
+ [],
113
+ 'constructor',
114
+ contract.constructorArgs.map(a => printArgument(a, helpers)),
115
+ contract.parents.flatMap(p => printParentConstructor(p, helpers)),
116
+ contract.constructorCode,
117
+ );
100
118
  }
101
119
  } else if (!helpers.upgradeable) {
102
120
  return [];
@@ -58,6 +58,12 @@ export function setUpgradeableGovernor(c: ContractBuilder, upgradeable: Upgradea
58
58
  });
59
59
  }
60
60
 
61
+ export function setUpgradeableAccount(c: ContractBuilder, upgradeable: Upgradeable) {
62
+ setUpgradeableBase(c, upgradeable, () => {
63
+ c.addModifier('onlyEntryPointOrSelf', functions._authorizeUpgrade);
64
+ });
65
+ }
66
+
61
67
  const functions = defineFunctions({
62
68
  _authorizeUpgrade: {
63
69
  args: [{ name: 'newImplementation', type: 'address' }],
package/src/signer.ts CHANGED
@@ -1,112 +1,91 @@
1
1
  import type { ContractBuilder } from './contract';
2
+ import { OptionsError } from './error';
3
+ import type { Upgradeable } from './set-upgradeable';
2
4
  import { defineFunctions } from './utils/define-functions';
3
5
 
4
6
  export const SignerOptions = [false, 'ERC7702', 'ECDSA', 'P256', 'RSA', 'Multisig', 'MultisigWeighted'] as const;
5
7
  export type SignerOptions = (typeof SignerOptions)[number];
6
8
 
7
- export function addSigner(c: ContractBuilder, signer: SignerOptions): void {
9
+ export function addSigner(c: ContractBuilder, signer: SignerOptions, upgradeable: Upgradeable): void {
8
10
  if (!signer) return;
9
11
 
10
- c.addParent(signers[signer]);
11
- c.addOverride(
12
- { name: signer === 'MultisigWeighted' ? signers.Multisig.name : signers[signer].name },
13
- signerFunctions._rawSignatureValidation,
14
- );
15
-
16
- // ERC-7702 doesn't require initialization
17
- if (signer === 'ERC7702') return;
18
-
19
- c.addParent({
20
- name: 'Initializable',
21
- path: '@openzeppelin/contracts/proxy/utils/Initializable.sol',
22
- });
23
-
24
- // Add locking constructor
25
- c.addNatspecTag('@custom:oz-upgrades-unsafe-allow', 'constructor');
26
- c.addConstructorCode(`_disableInitializers();`);
27
-
28
- // Add initializer
29
- const fn = signerFunctions[`initialize${signer}`];
30
- c.addModifier('initializer', fn);
12
+ const signerName = signer === 'MultisigWeighted' ? signers.Multisig.name : signers[signer].name;
13
+ c.addOverride({ name: signerName }, signerFunctions._rawSignatureValidation);
31
14
 
32
15
  switch (signer) {
33
- case 'Multisig':
34
- c.addFunctionCode(`_addSigners(${fn.args[0]!.name});`, fn);
35
- c.addFunctionCode(`_setThreshold(${fn.args[1]!.name});`, fn);
36
- break;
37
- case 'MultisigWeighted':
38
- c.addFunctionCode(`_addSigners(${fn.args[0]!.name});`, fn);
39
- c.addFunctionCode(`_setSignerWeights(${fn.args[0]!.name}, ${fn.args[1]!.name});`, fn);
40
- c.addFunctionCode(`_setThreshold(${fn.args[2]!.name});`, fn);
16
+ case 'ERC7702':
17
+ c.addParent(signers[signer]);
18
+ if (upgradeable) {
19
+ throw new OptionsError({
20
+ erc7702: 'EOAs can upgrade by redelegating to a new account',
21
+ upgradeable: 'EOAs can upgrade by redelegating to a new account',
22
+ });
23
+ }
41
24
  break;
42
25
  case 'ECDSA':
43
26
  case 'P256':
44
27
  case 'RSA':
45
- c.addFunctionCode(`_setSigner(${fn.args.map(({ name }) => name).join(', ')});`, fn);
28
+ case 'Multisig':
29
+ case 'MultisigWeighted': {
30
+ signerArgs[signer].forEach(arg => c.addConstructorArgument(arg));
31
+ c.addParent(
32
+ signers[signer],
33
+ signerArgs[signer].map(arg => ({ lit: arg.name })),
34
+ );
35
+ break;
36
+ }
46
37
  }
47
38
  }
48
39
 
49
40
  export const signers = {
50
41
  ERC7702: {
51
42
  name: 'SignerERC7702',
52
- path: '@openzeppelin/community-contracts/utils/cryptography/SignerERC7702.sol',
43
+ path: '@openzeppelin/contracts/utils/cryptography/signers/SignerERC7702.sol',
53
44
  },
54
45
  ECDSA: {
55
46
  name: 'SignerECDSA',
56
- path: '@openzeppelin/community-contracts/utils/cryptography/SignerECDSA.sol',
47
+ path: '@openzeppelin/contracts/utils/cryptography/signers/SignerECDSA.sol',
57
48
  },
58
49
  P256: {
59
50
  name: 'SignerP256',
60
- path: '@openzeppelin/community-contracts/utils/cryptography/SignerP256.sol',
51
+ path: '@openzeppelin/contracts/utils/cryptography/signers/SignerP256.sol',
61
52
  },
62
53
  RSA: {
63
54
  name: 'SignerRSA',
64
- path: '@openzeppelin/community-contracts/utils/cryptography/SignerRSA.sol',
55
+ path: '@openzeppelin/contracts/utils/cryptography/signers/SignerRSA.sol',
65
56
  },
66
57
  Multisig: {
67
58
  name: 'MultiSignerERC7913',
68
- path: '@openzeppelin/community-contracts/utils/cryptography/MultiSignerERC7913.sol',
59
+ path: '@openzeppelin/contracts/utils/cryptography/signers/MultiSignerERC7913.sol',
69
60
  },
70
61
  MultisigWeighted: {
71
62
  name: 'MultiSignerERC7913Weighted',
72
- path: '@openzeppelin/community-contracts/utils/cryptography/MultiSignerERC7913Weighted.sol',
63
+ path: '@openzeppelin/contracts/utils/cryptography/signers/MultiSignerERC7913Weighted.sol',
73
64
  },
74
65
  };
75
66
 
67
+ export const signerArgs: Record<Exclude<SignerOptions, false | 'ERC7702'>, { name: string; type: string }[]> = {
68
+ ECDSA: [{ name: 'signer', type: 'address' }],
69
+ P256: [
70
+ { name: 'qx', type: 'bytes32' },
71
+ { name: 'qy', type: 'bytes32' },
72
+ ],
73
+ RSA: [
74
+ { name: 'e', type: 'bytes memory' },
75
+ { name: 'n', type: 'bytes memory' },
76
+ ],
77
+ Multisig: [
78
+ { name: 'signers', type: 'bytes[] memory' },
79
+ { name: 'threshold', type: 'uint64' },
80
+ ],
81
+ MultisigWeighted: [
82
+ { name: 'signers', type: 'bytes[] memory' },
83
+ { name: 'weights', type: 'uint64[] memory' },
84
+ { name: 'threshold', type: 'uint64' },
85
+ ],
86
+ };
87
+
76
88
  export const signerFunctions = defineFunctions({
77
- initializeECDSA: {
78
- kind: 'public' as const,
79
- args: [{ name: 'signer', type: 'address' }],
80
- },
81
- initializeP256: {
82
- kind: 'public' as const,
83
- args: [
84
- { name: 'qx', type: 'bytes32' },
85
- { name: 'qy', type: 'bytes32' },
86
- ],
87
- },
88
- initializeRSA: {
89
- kind: 'public' as const,
90
- args: [
91
- { name: 'e', type: 'bytes memory' },
92
- { name: 'n', type: 'bytes memory' },
93
- ],
94
- },
95
- initializeMultisig: {
96
- kind: 'public' as const,
97
- args: [
98
- { name: 'signers', type: 'bytes[] memory' },
99
- { name: 'threshold', type: 'uint256' },
100
- ],
101
- },
102
- initializeMultisigWeighted: {
103
- kind: 'public' as const,
104
- args: [
105
- { name: 'signers', type: 'bytes[] memory' },
106
- { name: 'weights', type: 'uint256[] memory' },
107
- { name: 'threshold', type: 'uint256' },
108
- ],
109
- },
110
89
  _rawSignatureValidation: {
111
90
  kind: 'internal' as const,
112
91
  args: [
@@ -6,22 +6,82 @@ import SOLIDITY_VERSION from './solidity-version.json';
6
6
  import contracts from '../openzeppelin-contracts';
7
7
  import type { Lines } from './utils/format-lines';
8
8
  import { formatLinesWithSpaces, spaceBetween } from './utils/format-lines';
9
+ import type { Upgradeable } from './set-upgradeable';
9
10
 
10
11
  function getHeader(c: Contract) {
11
12
  return [`// SPDX-License-Identifier: ${c.license}`, `pragma solidity ^${SOLIDITY_VERSION};`];
12
13
  }
13
14
 
14
- const test = (c: Contract, opts?: GenericOptions) => {
15
- return formatLinesWithSpaces(2, ...spaceBetween(getHeader(c), getImports(c), getTestCase(c)));
15
+ function shouldUseUnsafeAllowConstructor(c: Contract): boolean {
16
+ // TODO: remove that selector when the upgrades plugin supports @custom:oz-upgrades-unsafe-allow-reachable
17
+ return c.parents.find(p => ['EIP712'].includes(p.contract.name)) !== undefined;
18
+ }
16
19
 
17
- function getImports(c: Contract) {
18
- const result = ['import {Test} from "forge-std/Test.sol";'];
19
- if (c.upgradeable) {
20
- result.push('import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";');
20
+ function getImports(c: Contract, prepopulateImports: string[]): string[] {
21
+ const result: string[] = [...prepopulateImports];
22
+ if (c.upgradeable) {
23
+ const unsafeAllowConstructor = shouldUseUnsafeAllowConstructor(c);
24
+
25
+ result.push(
26
+ unsafeAllowConstructor
27
+ ? 'import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol";'
28
+ : 'import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";',
29
+ );
30
+ }
31
+ result.push(`import {${c.name}} from "src/${c.name}.sol";`);
32
+ return result;
33
+ }
34
+
35
+ function getDeploymentCode(
36
+ c: Contract,
37
+ args: string[],
38
+ declareContractVariable: boolean,
39
+ upgradeable?: Upgradeable,
40
+ ): Lines[] {
41
+ const unsafeAllowConstructor = shouldUseUnsafeAllowConstructor(c);
42
+ const instanceDeclaration = declareContractVariable ? `${c.name} ` : '';
43
+
44
+ switch (upgradeable) {
45
+ case 'transparent':
46
+ return printDeployProxyAndAssignInstance('deployTransparentProxy', true);
47
+ case 'uups':
48
+ return printDeployProxyAndAssignInstance('deployUUPSProxy', false);
49
+ default:
50
+ return [`${instanceDeclaration}instance = new ${c.name}(${args.join(', ')});`];
51
+ }
52
+
53
+ function printDeployProxyAndAssignInstance(deployProxyFunctionName: string, includeInitialOwner: boolean) {
54
+ const deployProxyArgs = [`"${c.name}.sol"`];
55
+ if (includeInitialOwner) {
56
+ deployProxyArgs.push('initialOwner');
57
+ }
58
+ deployProxyArgs.push(`abi.encodeCall(${c.name}.initialize, (${args.join(', ')}))`);
59
+ if (unsafeAllowConstructor) {
60
+ deployProxyArgs.push('opts');
61
+ }
62
+ for (let i = 0; i < deployProxyArgs.length - 1; i++) {
63
+ deployProxyArgs[i] += ',';
21
64
  }
22
- result.push(`import {${c.name}} from "src/${c.name}.sol";`);
23
- return result;
65
+
66
+ return [
67
+ unsafeAllowConstructor && 'Options memory opts;',
68
+ unsafeAllowConstructor && 'opts.unsafeAllow = "constructor";',
69
+ `address proxy = Upgrades.${deployProxyFunctionName}(`,
70
+ deployProxyArgs,
71
+ ');',
72
+ // Account has a receive function, this requires a payable address
73
+ c.parents.find(p => ['Account'].includes(p.contract.name))
74
+ ? `${instanceDeclaration}instance = ${c.name}(payable(proxy));`
75
+ : `${instanceDeclaration}instance = ${c.name}(proxy);`,
76
+ ].filter(line => line !== false);
24
77
  }
78
+ }
79
+
80
+ const test = (c: Contract, opts?: GenericOptions) => {
81
+ return formatLinesWithSpaces(
82
+ 2,
83
+ ...spaceBetween(getHeader(c), getImports(c, ['import {Test} from "forge-std/Test.sol";']), getTestCase(c)),
84
+ );
25
85
 
26
86
  function getTestCase(c: Contract) {
27
87
  const args = getAddressArgs(c);
@@ -29,35 +89,18 @@ const test = (c: Contract, opts?: GenericOptions) => {
29
89
  `contract ${c.name}Test is Test {`,
30
90
  spaceBetween(
31
91
  [`${c.name} public instance;`],
32
- ['function setUp() public {', getAddressVariables(c, args), getDeploymentCode(c, args), '}'],
92
+ [
93
+ 'function setUp() public {',
94
+ getAddressVariables(c, args),
95
+ getDeploymentCode(c, args, false, opts?.upgradeable),
96
+ '}',
97
+ ],
33
98
  getContractSpecificTestFunction(),
34
99
  ),
35
100
  '}',
36
101
  ];
37
102
  }
38
103
 
39
- function getDeploymentCode(c: Contract, args: string[]): Lines[] {
40
- if (c.upgradeable) {
41
- if (opts?.upgradeable === 'transparent') {
42
- return [
43
- `address proxy = Upgrades.deployTransparentProxy(`,
44
- [`"${c.name}.sol",`, `initialOwner,`, `abi.encodeCall(${c.name}.initialize, (${args.join(', ')}))`],
45
- ');',
46
- `instance = ${c.name}(proxy);`,
47
- ];
48
- } else {
49
- return [
50
- `address proxy = Upgrades.deployUUPSProxy(`,
51
- [`"${c.name}.sol",`, `abi.encodeCall(${c.name}.initialize, (${args.join(', ')}))`],
52
- ');',
53
- `instance = ${c.name}(proxy);`,
54
- ];
55
- }
56
- } else {
57
- return [`instance = new ${c.name}(${args.join(', ')});`];
58
- }
59
- }
60
-
61
104
  function getAddressVariables(c: Contract, args: string[]): Lines[] {
62
105
  const vars = [];
63
106
  let i = 1; // private key index starts from 1 since it must be non-zero
@@ -80,6 +123,7 @@ const test = (c: Contract, opts?: GenericOptions) => {
80
123
  case 'ERC1155':
81
124
  return ['function testUri() public view {', [`assertEq(instance.uri(0), "${opts.uri}");`], '}'];
82
125
 
126
+ case 'Account':
83
127
  case 'Governor':
84
128
  case 'Custom':
85
129
  return ['function testSomething() public {', ['// Add your test here'], '}'];
@@ -103,23 +147,21 @@ function getAddressArgs(c: Contract): string[] {
103
147
  }
104
148
 
105
149
  const script = (c: Contract, opts?: GenericOptions) => {
106
- return formatLinesWithSpaces(2, ...spaceBetween(getHeader(c), getImports(c), getScript(c)));
107
-
108
- function getImports(c: Contract) {
109
- const result = ['import {Script} from "forge-std/Script.sol";', 'import {console} from "forge-std/console.sol";'];
110
- if (c.upgradeable) {
111
- result.push('import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";');
112
- }
113
- result.push(`import {${c.name}} from "src/${c.name}.sol";`);
114
- return result;
115
- }
150
+ return formatLinesWithSpaces(
151
+ 2,
152
+ ...spaceBetween(
153
+ getHeader(c),
154
+ getImports(c, ['import {Script} from "forge-std/Script.sol";', 'import {console} from "forge-std/console.sol";']),
155
+ getScript(c),
156
+ ),
157
+ );
116
158
 
117
159
  function getScript(c: Contract) {
118
160
  const args = getAddressArgs(c);
119
161
  const deploymentLines = [
120
162
  'vm.startBroadcast();',
121
163
  ...getAddressVariables(c, args),
122
- ...getDeploymentCode(c, args),
164
+ ...getDeploymentCode(c, args, true, opts?.upgradeable),
123
165
  `console.log("${c.upgradeable ? 'Proxy' : 'Contract'} deployed to %s", address(instance));`,
124
166
  'vm.stopBroadcast();',
125
167
  ];
@@ -133,28 +175,6 @@ const script = (c: Contract, opts?: GenericOptions) => {
133
175
  ];
134
176
  }
135
177
 
136
- function getDeploymentCode(c: Contract, args: string[]): Lines[] {
137
- if (c.upgradeable) {
138
- if (opts?.upgradeable === 'transparent') {
139
- return [
140
- `address proxy = Upgrades.deployTransparentProxy(`,
141
- [`"${c.name}.sol",`, `initialOwner,`, `abi.encodeCall(${c.name}.initialize, (${args.join(', ')}))`],
142
- ');',
143
- `${c.name} instance = ${c.name}(proxy);`,
144
- ];
145
- } else {
146
- return [
147
- `address proxy = Upgrades.deployUUPSProxy(`,
148
- [`"${c.name}.sol",`, `abi.encodeCall(${c.name}.initialize, (${args.join(', ')}))`],
149
- ');',
150
- `${c.name} instance = ${c.name}(proxy);`,
151
- ];
152
- }
153
- } else {
154
- return [`${c.name} instance = new ${c.name}(${args.join(', ')});`];
155
- }
156
- }
157
-
158
178
  function getAddressVariables(c: Contract, args: string[]): Lines[] {
159
179
  const vars = [];
160
180
  if (c.upgradeable && opts?.upgradeable === 'transparent' && !args.includes('initialOwner')) {
@@ -87,6 +87,7 @@ const test = (c: Contract, opts?: GenericOptions) => {
87
87
  case 'ERC1155':
88
88
  return [`expect(await instance.uri(0)).to.equal("${opts.uri}");`];
89
89
 
90
+ case 'Account':
90
91
  case 'Governor':
91
92
  case 'Custom':
92
93
  break;
@@ -118,9 +119,14 @@ function getAddressArgs(c: Contract): string[] {
118
119
  }
119
120
 
120
121
  function getDeploymentCall(c: Contract, args: string[]): string {
121
- return c.upgradeable
122
- ? `upgrades.deployProxy(ContractFactory, [${args.join(', ')}])`
123
- : `ContractFactory.deploy(${args.join(', ')})`;
122
+ // TODO: remove that selector when the upgrades plugin supports @custom:oz-upgrades-unsafe-allow-reachable
123
+ const unsafeAllowConstructor = c.parents.find(p => ['EIP712'].includes(p.contract.name)) !== undefined;
124
+
125
+ return !c.upgradeable
126
+ ? `ContractFactory.deploy(${args.join(', ')})`
127
+ : unsafeAllowConstructor
128
+ ? `upgrades.deployProxy(ContractFactory, [${args.join(', ')}], { unsafeAllow: 'constructor' })`
129
+ : `upgrades.deployProxy(ContractFactory, [${args.join(', ')}])`;
124
130
  }
125
131
 
126
132
  const script = (c: Contract) => {