@openzeppelin/wizard 0.10.4 → 0.10.5

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.
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "hardhat-sample",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "hardhat test"
8
+ },
9
+ "author": "",
10
+ "license": "ISC",
11
+ "devDependencies": {
12
+ "@openzeppelin/contracts": "^5.5.0",
13
+ "@parity/hardhat-polkadot": "^0.2.7",
14
+ "@nomicfoundation/hardhat-toolbox": "^6.1.0",
15
+ "hardhat": "^2.22.0"
16
+ }
17
+ }
@@ -1,5 +1,5 @@
1
1
  import type { CommonOptions } from './common-options';
2
- import contractsVersion from '../openzeppelin-contracts-version';
2
+ import contractsVersion from '../openzeppelin-contracts-version.json';
3
3
 
4
4
  export function getVersionedRemappings(opts?: CommonOptions): string[] {
5
5
  const remappings = [`@openzeppelin/contracts/=@openzeppelin/contracts@${contractsVersion.version}/`];
@@ -0,0 +1,86 @@
1
+ import type JSZip from 'jszip';
2
+ import type { Contract } from './contract';
3
+ import { HardhatZipGenerator } from './zip-hardhat';
4
+ import type { GenericOptions } from './build-generic';
5
+ import SOLIDITY_VERSION from './solidity-version.json';
6
+
7
+ class HardhatPolkadotZipGenerator extends HardhatZipGenerator {
8
+ protected getAdditionalHardhatImports(): string[] {
9
+ return ['@parity/hardhat-polkadot'];
10
+ }
11
+
12
+ protected getHardhatConfigJsonString(): string {
13
+ return `\
14
+ {
15
+ solidity: {
16
+ version: "${SOLIDITY_VERSION}",
17
+ settings: {
18
+ evmVersion: 'cancun',
19
+ optimizer: {
20
+ enabled: true,
21
+ },
22
+ },
23
+ },
24
+ resolc: {
25
+ compilerSource: 'npm',
26
+ },
27
+ networks: {
28
+ hardhat: {
29
+ polkavm: true,
30
+ nodeConfig: {
31
+ nodeBinaryPath: 'INSERT_PATH_TO_REVIVE_DEV_NODE',
32
+ rpcPort: 8000,
33
+ dev: true,
34
+ },
35
+ adapterConfig: {
36
+ adapterBinaryPath: 'INSERT_PATH_TO_ETH_RPC_ADAPTER',
37
+ dev: true,
38
+ },
39
+ },
40
+ },
41
+ }`;
42
+ }
43
+
44
+ protected async getPackageJson(c: Contract): Promise<unknown> {
45
+ const { default: packageJson } = await import('./environments/hardhat/polkadot/package.json');
46
+ packageJson.license = c.license;
47
+ return packageJson;
48
+ }
49
+
50
+ protected async getPackageLock(c: Contract): Promise<unknown> {
51
+ const { default: packageLock } = await import('./environments/hardhat/polkadot/package-lock.json');
52
+ packageLock.packages[''].license = c.license;
53
+ return packageLock;
54
+ }
55
+
56
+ protected getReadmePrerequisitesSection(): string {
57
+ return `\
58
+ ## Prerequisites
59
+
60
+ Ensure you have the following installed:
61
+ - [Node.js 22.5+](https://nodejs.org/en/download/)
62
+ - npm 10.9.0+
63
+
64
+ `;
65
+ }
66
+
67
+ protected getReadmeTestingEnvironmentSetupSection(): string {
68
+ return `\
69
+ ## Setting up a testing environment
70
+
71
+ Follow the steps in [Polkadot's documentation](https://docs.polkadot.com/smart-contracts/dev-environments/local-dev-node/) to set up a local development node and replace the placeholder values \`INSERT_PATH_TO_REVIVE_DEV_NODE\` and \`INSERT_PATH_TO_ETH_RPC_ADAPTER\` in \`hardhat.config.ts\`.
72
+
73
+ `;
74
+ }
75
+
76
+ protected getGitIgnoreHardhatIgnition(): string {
77
+ return `
78
+ # Hardhat Ignition default folder for deployments against a local Polkadot Revive Dev node
79
+ ignition/deployments/chain-420420420
80
+ `;
81
+ }
82
+ }
83
+
84
+ export async function zipHardhatPolkadot(c: Contract, opts?: GenericOptions): Promise<JSZip> {
85
+ return new HardhatPolkadotZipGenerator().zipHardhat(c, opts);
86
+ }
@@ -1,72 +1,31 @@
1
1
  import JSZip from 'jszip';
2
2
  import type { GenericOptions } from './build-generic';
3
- import type { Contract } from './contract';
3
+ import type { Contract, FunctionArgument } from './contract';
4
4
  import { printContract } from './print';
5
5
  import SOLIDITY_VERSION from './solidity-version.json';
6
6
  import type { Lines } from './utils/format-lines';
7
7
  import { formatLinesWithSpaces, spaceBetween } from './utils/format-lines';
8
8
 
9
- const hardhatConfig = (upgradeable: boolean) => `\
10
- import { HardhatUserConfig } from "hardhat/config";
11
- import "@nomicfoundation/hardhat-toolbox";
12
- ${upgradeable ? `import "@openzeppelin/hardhat-upgrades";` : ''}
13
-
14
- const config: HardhatUserConfig = {
15
- solidity: {
16
- version: "${SOLIDITY_VERSION}",
17
- settings: {
18
- evmVersion: 'cancun',
19
- optimizer: {
20
- enabled: true,
21
- },
22
- },
23
- },
24
- };
9
+ class TestGenerator {
10
+ constructor(private parent: HardhatZipGenerator) {}
25
11
 
26
- export default config;
27
- `;
28
-
29
- const tsConfig = `\
30
- {
31
- "compilerOptions": {
32
- "target": "es2020",
33
- "module": "commonjs",
34
- "esModuleInterop": true,
35
- "forceConsistentCasingInFileNames": true,
36
- "strict": true,
37
- "skipLibCheck": true,
38
- "resolveJsonModule": true
12
+ getContent(c: Contract, opts?: GenericOptions): string {
13
+ return formatLinesWithSpaces(2, ...spaceBetween(this.getImports(c), this.getTestCase(c, opts)));
39
14
  }
40
- }
41
- `;
42
15
 
43
- const gitIgnore = `\
44
- node_modules
45
- .env
46
- coverage
47
- coverage.json
48
- typechain
49
- typechain-types
50
-
51
- # Hardhat files
52
- cache
53
- artifacts
54
- `;
55
-
56
- const test = (c: Contract, opts?: GenericOptions) => {
57
- return formatLinesWithSpaces(2, ...spaceBetween(getImports(c), getTestCase(c)));
58
-
59
- function getTestCase(c: Contract) {
60
- const args = getAddressArgs(c);
16
+ private getTestCase(c: Contract, opts?: GenericOptions): Lines[] {
61
17
  return [
62
18
  `describe("${c.name}", function () {`,
63
19
  [
64
20
  'it("Test contract", async function () {',
65
21
  spaceBetween(
66
22
  [`const ContractFactory = await ethers.getContractFactory("${c.name}");`],
67
- getAddressVariables(args),
68
- [`const instance = await ${getDeploymentCall(c, args)};`, 'await instance.waitForDeployment();'],
69
- getExpects(),
23
+ this.declareVariables(c.constructorArgs),
24
+ this.getDeployLines(
25
+ c,
26
+ c.constructorArgs.map(a => a.name),
27
+ ),
28
+ this.getExpects(opts),
70
29
  ),
71
30
  '});',
72
31
  ],
@@ -74,25 +33,25 @@ const test = (c: Contract, opts?: GenericOptions) => {
74
33
  ];
75
34
  }
76
35
 
77
- function getImports(c: Contract) {
78
- return ['import { expect } from "chai";', `import { ${getHardhatPlugins(c).join(', ')} } from "hardhat";`];
36
+ private getImports(c: Contract): Lines[] {
37
+ return [
38
+ 'import { expect } from "chai";',
39
+ `import { ${this.parent.getHardhatPlugins(c).join(', ')} } from "hardhat";`,
40
+ ];
79
41
  }
80
42
 
81
- function getExpects(): Lines[] {
43
+ private getExpects(opts?: GenericOptions): Lines[] {
82
44
  if (opts !== undefined) {
83
45
  switch (opts.kind) {
84
46
  case 'ERC20':
85
47
  case 'ERC721':
86
48
  return [`expect(await instance.name()).to.equal("${opts.name}");`];
87
-
88
49
  case 'ERC1155':
89
50
  return [`expect(await instance.uri(0)).to.equal("${opts.uri}");`];
90
-
91
51
  case 'Account':
92
52
  case 'Governor':
93
53
  case 'Custom':
94
54
  break;
95
-
96
55
  default:
97
56
  throw new Error('Unknown ERC');
98
57
  }
@@ -100,46 +59,133 @@ const test = (c: Contract, opts?: GenericOptions) => {
100
59
  return [];
101
60
  }
102
61
 
103
- function getAddressVariables(args: string[]): Lines[] {
104
- const vars = [];
105
- for (let i = 0; i < args.length; i++) {
106
- vars.push(`const ${args[i]} = (await ethers.getSigners())[${i}].address;`);
107
- }
108
- return vars;
62
+ private declareVariables(args: FunctionArgument[]): Lines[] {
63
+ return args.flatMap((arg, i) => {
64
+ if (arg.type === 'address') {
65
+ return [`const ${arg.name} = (await ethers.getSigners())[${i}].address;`];
66
+ } else {
67
+ return [`// TODO: Set the following constructor argument`, `// const ${arg.name} = ...;`];
68
+ }
69
+ });
109
70
  }
110
- };
111
71
 
112
- function getAddressArgs(c: Contract): string[] {
113
- const args = [];
114
- for (const constructorArg of c.constructorArgs) {
115
- if (constructorArg.type === 'address') {
116
- args.push(constructorArg.name);
72
+ private getDeployLines(c: Contract, argNames: string[]): Lines[] {
73
+ if (c.constructorArgs.some(a => a.type !== 'address')) {
74
+ return [
75
+ `// TODO: Uncomment the below when the missing constructor arguments are set above`,
76
+ `// const instance = await ${this.parent.getDeploymentCall(c, argNames)};`,
77
+ `// await instance.waitForDeployment();`,
78
+ ];
79
+ } else {
80
+ return [
81
+ `const instance = await ${this.parent.getDeploymentCall(c, argNames)};`,
82
+ 'await instance.waitForDeployment();',
83
+ ];
117
84
  }
118
85
  }
119
- return args;
120
86
  }
121
87
 
122
- function getDeploymentCall(c: Contract, args: string[]): string {
123
- // TODO: remove that selector when the upgrades plugin supports @custom:oz-upgrades-unsafe-allow-reachable
124
- const unsafeAllowConstructor = c.parents.find(p => ['EIP712'].includes(p.contract.name)) !== undefined;
88
+ export class HardhatZipGenerator {
89
+ protected getAdditionalHardhatImports(): string[] {
90
+ return [];
91
+ }
92
+
93
+ protected getHardhatConfigJsonString(): string {
94
+ return `\
95
+ {
96
+ solidity: {
97
+ version: "${SOLIDITY_VERSION}",
98
+ settings: {
99
+ evmVersion: 'cancun',
100
+ optimizer: {
101
+ enabled: true,
102
+ },
103
+ },
104
+ },
105
+ }`;
106
+ }
107
+
108
+ protected getHardhatConfig(upgradeable: boolean): string {
109
+ const additionalImports = this.getAdditionalHardhatImports();
110
+ const importsSection =
111
+ additionalImports.length > 0 ? additionalImports.map(imp => `import "${imp}";`).join('\n') + '\n' : '';
125
112
 
126
- return !c.upgradeable
127
- ? `ContractFactory.deploy(${args.join(', ')})`
128
- : unsafeAllowConstructor
129
- ? `upgrades.deployProxy(ContractFactory, [${args.join(', ')}], { unsafeAllow: 'constructor' })`
130
- : `upgrades.deployProxy(ContractFactory, [${args.join(', ')}])`;
113
+ return `\
114
+ import { HardhatUserConfig } from "hardhat/config";
115
+ import "@nomicfoundation/hardhat-toolbox";
116
+ ${importsSection}${upgradeable ? `import "@openzeppelin/hardhat-upgrades";` : ''}
117
+
118
+ const config: HardhatUserConfig = ${this.getHardhatConfigJsonString()};
119
+
120
+ export default config;
121
+ `;
122
+ }
123
+
124
+ protected getTsConfig(): string {
125
+ return `\
126
+ {
127
+ "compilerOptions": {
128
+ "target": "es2020",
129
+ "module": "commonjs",
130
+ "esModuleInterop": true,
131
+ "forceConsistentCasingInFileNames": true,
132
+ "strict": true,
133
+ "skipLibCheck": true,
134
+ "resolveJsonModule": true
135
+ }
131
136
  }
137
+ `;
138
+ }
132
139
 
133
- const script = (c: Contract) => {
134
- const args = getAddressArgs(c);
135
- return `\
136
- import { ${getHardhatPlugins(c).join(', ')} } from "hardhat";
140
+ protected getGitIgnoreHardhatIgnition(): string {
141
+ return `
142
+ # Hardhat Ignition default folder for deployments against a local node
143
+ ignition/deployments/chain-31337
144
+ `;
145
+ }
146
+
147
+ protected getGitIgnore(): string {
148
+ return `\
149
+ node_modules
150
+ .env
151
+ coverage
152
+ coverage.json
153
+ typechain
154
+ typechain-types
155
+
156
+ # Hardhat files
157
+ cache
158
+ artifacts
159
+ ${this.getGitIgnoreHardhatIgnition()}`;
160
+ }
161
+
162
+ protected getTest(c: Contract, opts?: GenericOptions): string {
163
+ return new TestGenerator(this).getContent(c, opts);
164
+ }
165
+
166
+ public getDeploymentCall(c: Contract, args: string[]): string {
167
+ // TODO: remove that selector when the upgrades plugin supports @custom:oz-upgrades-unsafe-allow-reachable
168
+ const unsafeAllowConstructor = c.parents.find(p => ['EIP712'].includes(p.contract.name)) !== undefined;
169
+
170
+ return !c.upgradeable
171
+ ? `ContractFactory.deploy(${args.join(', ')})`
172
+ : unsafeAllowConstructor
173
+ ? `upgrades.deployProxy(ContractFactory, [${args.join(', ')}], { unsafeAllow: ['constructor'] })`
174
+ : `upgrades.deployProxy(ContractFactory, [${args.join(', ')}])`;
175
+ }
176
+
177
+ protected getScript(c: Contract): string {
178
+ return `\
179
+ import { ${this.getHardhatPlugins(c).join(', ')} } from "hardhat";
137
180
 
138
181
  async function main() {
139
182
  const ContractFactory = await ethers.getContractFactory("${c.name}");
140
183
 
141
- ${args.length > 0 ? '// TODO: Set addresses for the contract arguments below' : ''}
142
- const instance = await ${getDeploymentCall(c, args)};
184
+ ${c.constructorArgs.length > 0 ? '// TODO: Set values for the constructor arguments below' : ''}
185
+ const instance = await ${this.getDeploymentCall(
186
+ c,
187
+ c.constructorArgs.map(a => a.name),
188
+ )};
143
189
  await instance.waitForDeployment();
144
190
 
145
191
  console.log(\`${c.upgradeable ? 'Proxy' : 'Contract'} deployed to \${await instance.getAddress()}\`);
@@ -152,38 +198,47 @@ main().catch((error) => {
152
198
  process.exitCode = 1;
153
199
  });
154
200
  `;
155
- };
156
-
157
- const lowerFirstCharacter = (str: string) => str.charAt(0).toLowerCase() + str.slice(1);
201
+ }
158
202
 
159
- const ignitionModule = (c: Contract) => {
160
- const deployArguments = getAddressArgs(c);
161
- const contractVariableName = lowerFirstCharacter(c.name);
203
+ private lowerFirstCharacter(str: string): string {
204
+ return str.charAt(0).toLowerCase() + str.slice(1);
205
+ }
162
206
 
163
- return `import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
207
+ protected getIgnitionModule(c: Contract): string {
208
+ const contractVariableName = this.lowerFirstCharacter(c.name);
209
+ return `import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
164
210
 
165
211
  export default buildModule("${c.name}Module", (m) => {
166
212
 
167
- ${deployArguments.length > 0 ? '// TODO: Set addresses for the contract arguments below' : ''}
168
- const ${contractVariableName} = m.contract("${c.name}", [${deployArguments.join(', ')}]);
213
+ ${c.constructorArgs.length > 0 ? '// TODO: Set values for the constructor arguments below' : ''}
214
+ const ${contractVariableName} = m.contract("${c.name}", [${c.constructorArgs.map(a => a.name).join(', ')}]);
169
215
 
170
216
  return { ${contractVariableName} };
171
217
  });
172
218
  `;
173
- };
219
+ }
220
+
221
+ protected getReadmeTestingEnvironmentSetupSection(): string {
222
+ return '';
223
+ }
174
224
 
175
- const readme = (c: Contract) => `\
225
+ protected getReadmePrerequisitesSection(): string {
226
+ return '';
227
+ }
228
+
229
+ protected getReadme(c: Contract): string {
230
+ return `\
176
231
  # Sample Hardhat Project
177
232
 
178
233
  This project demonstrates a basic Hardhat use case. It comes with a contract generated by [OpenZeppelin Wizard](https://wizard.openzeppelin.com/), a test for that contract, ${c.upgradeable ? 'and a script that deploys that contract' : 'and a Hardhat Ignition module that deploys that contract'}.
179
234
 
180
- ## Installing dependencies
235
+ ${this.getReadmePrerequisitesSection()}## Installing dependencies
181
236
 
182
237
  \`\`\`
183
238
  npm install
184
239
  \`\`\`
185
240
 
186
- ## Testing the contract
241
+ ${this.getReadmeTestingEnvironmentSetupSection()}## Testing the contract
187
242
 
188
243
  \`\`\`
189
244
  npm test
@@ -197,43 +252,62 @@ You can target any network from your Hardhat config using:
197
252
  ${c.upgradeable ? 'npx hardhat run --network <network-name> scripts/deploy.ts' : `npx hardhat ignition deploy ignition/modules/${c.name}.ts --network <network-name>`}
198
253
  \`\`\`
199
254
  `;
255
+ }
200
256
 
201
- function getHardhatPlugins(c: Contract) {
202
- const plugins = ['ethers'];
203
- if (c.upgradeable) {
204
- plugins.push('upgrades');
257
+ public getHardhatPlugins(c: Contract): string[] {
258
+ const plugins = ['ethers'];
259
+ if (c.upgradeable) {
260
+ plugins.push('upgrades');
261
+ }
262
+ return plugins;
205
263
  }
206
- return plugins;
207
- }
208
264
 
209
- export async function zipHardhat(c: Contract, opts?: GenericOptions) {
210
- const zip = new JSZip();
265
+ protected async getPackageJson(c: Contract): Promise<unknown> {
266
+ const { default: packageJson } = c.upgradeable
267
+ ? await import('./environments/hardhat/upgradeable/package.json')
268
+ : await import('./environments/hardhat/package.json');
269
+ packageJson.license = c.license;
270
+ return packageJson;
271
+ }
211
272
 
212
- const { default: packageJson } = c.upgradeable
213
- ? await import('./environments/hardhat/upgradeable/package.json')
214
- : await import('./environments/hardhat/package.json');
215
- packageJson.license = c.license;
273
+ protected async getPackageLock(c: Contract): Promise<unknown> {
274
+ const { default: packageLock } = c.upgradeable
275
+ ? await import('./environments/hardhat/upgradeable/package-lock.json')
276
+ : await import('./environments/hardhat/package-lock.json');
277
+ packageLock.packages[''].license = c.license;
278
+ return packageLock;
279
+ }
216
280
 
217
- const { default: packageLock } = c.upgradeable
218
- ? await import('./environments/hardhat/upgradeable/package-lock.json')
219
- : await import('./environments/hardhat/package-lock.json');
220
- packageLock.packages[''].license = c.license;
281
+ protected getPrintContract(c: Contract): string {
282
+ return printContract(c);
283
+ }
221
284
 
222
- zip.file(`contracts/${c.name}.sol`, printContract(c));
223
- zip.file('test/test.ts', test(c, opts));
285
+ async zipHardhat(c: Contract, opts?: GenericOptions): Promise<JSZip> {
286
+ const zip = new JSZip();
224
287
 
225
- if (c.upgradeable) {
226
- zip.file('scripts/deploy.ts', script(c));
227
- } else {
228
- zip.file(`ignition/modules/${c.name}.ts`, ignitionModule(c));
229
- }
288
+ const packageJson = await this.getPackageJson(c);
289
+ const packageLock = await this.getPackageLock(c);
290
+
291
+ zip.file(`contracts/${c.name}.sol`, this.getPrintContract(c));
292
+ zip.file('test/test.ts', this.getTest(c, opts));
293
+
294
+ if (c.upgradeable) {
295
+ zip.file('scripts/deploy.ts', this.getScript(c));
296
+ } else {
297
+ zip.file(`ignition/modules/${c.name}.ts`, this.getIgnitionModule(c));
298
+ }
230
299
 
231
- zip.file('.gitignore', gitIgnore);
232
- zip.file('hardhat.config.ts', hardhatConfig(c.upgradeable));
233
- zip.file('package.json', JSON.stringify(packageJson, null, 2));
234
- zip.file(`package-lock.json`, JSON.stringify(packageLock, null, 2));
235
- zip.file('README.md', readme(c));
236
- zip.file('tsconfig.json', tsConfig);
300
+ zip.file('.gitignore', this.getGitIgnore());
301
+ zip.file('hardhat.config.ts', this.getHardhatConfig(c.upgradeable));
302
+ zip.file('package.json', JSON.stringify(packageJson, null, 2));
303
+ zip.file(`package-lock.json`, JSON.stringify(packageLock, null, 2));
304
+ zip.file('README.md', this.getReadme(c));
305
+ zip.file('tsconfig.json', this.getTsConfig());
306
+
307
+ return zip;
308
+ }
309
+ }
237
310
 
238
- return zip;
311
+ export async function zipHardhat(c: Contract, opts?: GenericOptions): Promise<JSZip> {
312
+ return new HardhatZipGenerator().zipHardhat(c, opts);
239
313
  }