@openzeppelin/wizard 0.4.1 → 0.4.3

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 (77) hide show
  1. package/dist/contract.d.ts +2 -0
  2. package/dist/contract.d.ts.map +1 -1
  3. package/dist/contract.js +8 -0
  4. package/dist/contract.js.map +1 -1
  5. package/dist/environments/hardhat/package-lock.json +2999 -5190
  6. package/dist/environments/hardhat/package.json +1 -1
  7. package/dist/environments/hardhat/upgradeable/package-lock.json +1875 -3396
  8. package/dist/environments/hardhat/upgradeable/package.json +2 -2
  9. package/dist/erc20.d.ts +6 -1
  10. package/dist/erc20.d.ts.map +1 -1
  11. package/dist/erc20.js +5 -2
  12. package/dist/erc20.js.map +1 -1
  13. package/dist/erc721.d.ts +6 -1
  14. package/dist/erc721.d.ts.map +1 -1
  15. package/dist/erc721.js +6 -3
  16. package/dist/erc721.js.map +1 -1
  17. package/dist/generate/erc20.d.ts.map +1 -1
  18. package/dist/generate/erc20.js +2 -1
  19. package/dist/generate/erc20.js.map +1 -1
  20. package/dist/generate/erc721.d.ts.map +1 -1
  21. package/dist/generate/erc721.js +2 -1
  22. package/dist/generate/erc721.js.map +1 -1
  23. package/dist/generate/governor.d.ts.map +1 -1
  24. package/dist/generate/governor.js +2 -0
  25. package/dist/generate/governor.js.map +1 -1
  26. package/dist/generate/sources.d.ts +5 -4
  27. package/dist/generate/sources.d.ts.map +1 -1
  28. package/dist/generate/sources.js +27 -17
  29. package/dist/generate/sources.js.map +1 -1
  30. package/dist/governor.d.ts +2 -0
  31. package/dist/governor.d.ts.map +1 -1
  32. package/dist/governor.js +41 -8
  33. package/dist/governor.js.map +1 -1
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/print.d.ts.map +1 -1
  39. package/dist/print.js +6 -4
  40. package/dist/print.js.map +1 -1
  41. package/dist/set-clock-mode.d.ts +6 -0
  42. package/dist/set-clock-mode.d.ts.map +1 -0
  43. package/dist/set-clock-mode.js +31 -0
  44. package/dist/set-clock-mode.js.map +1 -0
  45. package/dist/test.js +23 -5
  46. package/dist/test.js.map +1 -1
  47. package/dist/utils/duration.d.ts +1 -0
  48. package/dist/utils/duration.d.ts.map +1 -1
  49. package/dist/utils/duration.js +15 -1
  50. package/dist/utils/duration.js.map +1 -1
  51. package/dist/utils/version.d.ts +5 -0
  52. package/dist/utils/version.d.ts.map +1 -0
  53. package/dist/utils/version.js +8 -0
  54. package/dist/utils/version.js.map +1 -0
  55. package/dist/zip-foundry.d.ts.map +1 -1
  56. package/dist/zip-foundry.js +141 -32
  57. package/dist/zip-foundry.js.map +1 -1
  58. package/package.json +5 -3
  59. package/src/contract.ts +10 -0
  60. package/src/environments/hardhat/package-lock.json +2999 -5190
  61. package/src/environments/hardhat/package.json +1 -1
  62. package/src/environments/hardhat/upgradeable/package-lock.json +1875 -3396
  63. package/src/environments/hardhat/upgradeable/package.json +2 -2
  64. package/src/erc20.ts +11 -3
  65. package/src/erc721.ts +12 -4
  66. package/src/generate/erc20.ts +2 -1
  67. package/src/generate/erc721.ts +2 -1
  68. package/src/generate/governor.ts +2 -0
  69. package/src/generate/sources.ts +30 -18
  70. package/src/governor.ts +40 -10
  71. package/src/index.ts +3 -1
  72. package/src/print.ts +6 -2
  73. package/src/set-clock-mode.ts +33 -0
  74. package/src/test.ts +34 -6
  75. package/src/utils/duration.ts +18 -0
  76. package/src/utils/version.ts +4 -0
  77. package/src/zip-foundry.ts +141 -34
@@ -11,8 +11,8 @@
11
11
  "devDependencies": {
12
12
  "@openzeppelin/contracts": "^5.0.0",
13
13
  "@openzeppelin/contracts-upgradeable": "^5.0.0",
14
- "@openzeppelin/hardhat-upgrades": "^2.0.0",
15
- "@nomicfoundation/hardhat-toolbox": "^3.0.0",
14
+ "@openzeppelin/hardhat-upgrades": "^3.0.0",
15
+ "@nomicfoundation/hardhat-toolbox": "^5.0.0",
16
16
  "hardhat": "^2.16.1"
17
17
  }
18
18
  }
package/src/erc20.ts CHANGED
@@ -6,6 +6,7 @@ import { CommonOptions, withCommonDefaults, defaults as commonDefaults } from '.
6
6
  import { setUpgradeable } from './set-upgradeable';
7
7
  import { setInfo } from './set-info';
8
8
  import { printContract } from './print';
9
+ import { ClockMode, clockModeDefault, setClockMode } from './set-clock-mode';
9
10
 
10
11
  export interface ERC20Options extends CommonOptions {
11
12
  name: string;
@@ -15,7 +16,11 @@ export interface ERC20Options extends CommonOptions {
15
16
  premint?: string;
16
17
  mintable?: boolean;
17
18
  permit?: boolean;
18
- votes?: boolean;
19
+ /**
20
+ * Whether to keep track of historical balances for voting in on-chain governance, and optionally specify the clock mode.
21
+ * Setting `true` is equivalent to 'blocknumber'. Setting a clock mode implies voting is enabled.
22
+ */
23
+ votes?: boolean | ClockMode;
19
24
  flashmint?: boolean;
20
25
  }
21
26
 
@@ -87,7 +92,8 @@ export function buildERC20(opts: ERC20Options): Contract {
87
92
  }
88
93
 
89
94
  if (allOpts.votes) {
90
- addVotes(c);
95
+ const clockMode = allOpts.votes === true ? clockModeDefault : allOpts.votes;
96
+ addVotes(c, clockMode);
91
97
  }
92
98
 
93
99
  if (allOpts.flashmint) {
@@ -166,7 +172,7 @@ function addPermit(c: ContractBuilder, name: string) {
166
172
 
167
173
  }
168
174
 
169
- function addVotes(c: ContractBuilder) {
175
+ function addVotes(c: ContractBuilder, clockMode: ClockMode) {
170
176
  if (!c.parents.some(p => p.contract.name === 'ERC20Permit')) {
171
177
  throw new Error('Missing ERC20Permit requirement for ERC20Votes');
172
178
  }
@@ -180,6 +186,8 @@ function addVotes(c: ContractBuilder) {
180
186
  c.addOverride({
181
187
  name: 'Nonces',
182
188
  }, functions.nonces);
189
+
190
+ setClockMode(c, ERC20Votes, clockMode);
183
191
  }
184
192
 
185
193
  function addFlashMint(c: ContractBuilder) {
package/src/erc721.ts CHANGED
@@ -7,6 +7,7 @@ import { CommonOptions, withCommonDefaults, defaults as commonDefaults } from '.
7
7
  import { setUpgradeable } from './set-upgradeable';
8
8
  import { setInfo } from './set-info';
9
9
  import { printContract } from './print';
10
+ import { ClockMode, clockModeDefault, setClockMode } from './set-clock-mode';
10
11
 
11
12
  export interface ERC721Options extends CommonOptions {
12
13
  name: string;
@@ -18,7 +19,11 @@ export interface ERC721Options extends CommonOptions {
18
19
  pausable?: boolean;
19
20
  mintable?: boolean;
20
21
  incremental?: boolean;
21
- votes?: boolean;
22
+ /**
23
+ * Whether to keep track of individual units for voting in on-chain governance, and optionally specify the clock mode.
24
+ * Setting `true` is equivalent to 'blocknumber'. Setting a clock mode implies voting is enabled.
25
+ */
26
+ votes?: boolean | ClockMode;
22
27
  }
23
28
 
24
29
  export const defaults: Required<ERC721Options> = {
@@ -34,7 +39,7 @@ export const defaults: Required<ERC721Options> = {
34
39
  votes: false,
35
40
  access: commonDefaults.access,
36
41
  upgradeable: commonDefaults.upgradeable,
37
- info: commonDefaults.info
42
+ info: commonDefaults.info,
38
43
  } as const;
39
44
 
40
45
  function withDefaults(opts: ERC721Options): Required<ERC721Options> {
@@ -94,7 +99,8 @@ export function buildERC721(opts: ERC721Options): Contract {
94
99
  }
95
100
 
96
101
  if (allOpts.votes) {
97
- addVotes(c, allOpts.name);
102
+ const clockMode = allOpts.votes === true ? clockModeDefault : allOpts.votes;
103
+ addVotes(c, allOpts.name, clockMode);
98
104
  }
99
105
 
100
106
  setAccessControl(c, access);
@@ -181,7 +187,7 @@ function addMintable(c: ContractBuilder, access: Access, incremental = false, ur
181
187
  }
182
188
  }
183
189
 
184
- function addVotes(c: ContractBuilder, name: string) {
190
+ function addVotes(c: ContractBuilder, name: string, clockMode: ClockMode) {
185
191
  const EIP712 = {
186
192
  name: 'EIP712',
187
193
  path: '@openzeppelin/contracts/utils/cryptography/EIP712.sol',
@@ -196,6 +202,8 @@ function addVotes(c: ContractBuilder, name: string) {
196
202
 
197
203
  c.addOverride(ERC721Votes, functions._update);
198
204
  c.addOverride(ERC721Votes, functions._increaseBalance);
205
+
206
+ setClockMode(c, ERC721Votes, clockMode);
199
207
  }
200
208
 
201
209
  const functions = defineFunctions({
@@ -1,5 +1,6 @@
1
1
  import type { ERC20Options } from '../erc20';
2
2
  import { accessOptions } from '../set-access-control';
3
+ import { clockModeOptions } from '../set-clock-mode';
3
4
  import { infoOptions } from '../set-info';
4
5
  import { upgradeableOptions } from '../set-upgradeable';
5
6
  import { generateAlternatives } from './alternatives';
@@ -13,7 +14,7 @@ const blueprint = {
13
14
  pausable: booleans,
14
15
  mintable: booleans,
15
16
  permit: booleans,
16
- votes: booleans,
17
+ votes: [ ...booleans, ...clockModeOptions ] as const,
17
18
  flashmint: booleans,
18
19
  premint: ['1'],
19
20
  access: accessOptions,
@@ -1,5 +1,6 @@
1
1
  import type { ERC721Options } from '../erc721';
2
2
  import { accessOptions } from '../set-access-control';
3
+ import { clockModeOptions } from '../set-clock-mode';
3
4
  import { infoOptions } from '../set-info';
4
5
  import { upgradeableOptions } from '../set-upgradeable';
5
6
  import { generateAlternatives } from './alternatives';
@@ -19,7 +20,7 @@ const blueprint = {
19
20
  access: accessOptions,
20
21
  upgradeable: upgradeableOptions,
21
22
  info: infoOptions,
22
- votes: booleans,
23
+ votes: [ ...booleans, ...clockModeOptions ] as const,
23
24
  };
24
25
 
25
26
  export function* generateERC721Options(): Generator<Required<ERC721Options>> {
@@ -1,5 +1,6 @@
1
1
  import { defaults, GovernorOptions, timelockOptions, votesOptions } from '../governor';
2
2
  import { accessOptions } from '../set-access-control';
3
+ import { clockModeOptions } from '../set-clock-mode';
3
4
  import { infoOptions } from '../set-info';
4
5
  import { upgradeableOptions } from '../set-upgradeable';
5
6
  import { generateAlternatives } from './alternatives';
@@ -17,6 +18,7 @@ const blueprint = {
17
18
  quorumPercent: [4],
18
19
  quorumAbsolute: ['1000'],
19
20
  votes: votesOptions,
21
+ clockMode: clockModeOptions,
20
22
  timelock: timelockOptions,
21
23
  storage: booleans,
22
24
  settings: booleans,
@@ -7,7 +7,7 @@ import { generateERC721Options } from './erc721';
7
7
  import { generateERC1155Options } from './erc1155';
8
8
  import { generateGovernorOptions } from './governor';
9
9
  import { generateCustomOptions } from './custom';
10
- import { buildGeneric, GenericOptions } from '../build-generic';
10
+ import { buildGeneric, GenericOptions, KindedOptions } from '../build-generic';
11
11
  import { printContract } from '../print';
12
12
  import { OptionsError } from '../error';
13
13
  import { findCover } from '../utils/find-cover';
@@ -15,25 +15,37 @@ import type { Contract } from '../contract';
15
15
 
16
16
  type Subset = 'all' | 'minimal-cover';
17
17
 
18
- export function* generateOptions(): Generator<GenericOptions> {
19
- for (const kindOpts of generateERC20Options()) {
20
- yield { kind: 'ERC20', ...kindOpts };
18
+ type Kind = keyof KindedOptions;
19
+
20
+ export function* generateOptions(kind?: Kind): Generator<GenericOptions> {
21
+ if (!kind || kind === 'ERC20') {
22
+ for (const kindOpts of generateERC20Options()) {
23
+ yield { kind: 'ERC20', ...kindOpts };
24
+ }
21
25
  }
22
26
 
23
- for (const kindOpts of generateERC721Options()) {
24
- yield { kind: 'ERC721', ...kindOpts };
27
+ if (!kind || kind === 'ERC721') {
28
+ for (const kindOpts of generateERC721Options()) {
29
+ yield { kind: 'ERC721', ...kindOpts };
30
+ }
25
31
  }
26
32
 
27
- for (const kindOpts of generateERC1155Options()) {
28
- yield { kind: 'ERC1155', ...kindOpts };
33
+ if (!kind || kind === 'ERC1155') {
34
+ for (const kindOpts of generateERC1155Options()) {
35
+ yield { kind: 'ERC1155', ...kindOpts };
36
+ }
29
37
  }
30
38
 
31
- for (const kindOpts of generateGovernorOptions()) {
32
- yield { kind: 'Governor', ...kindOpts };
39
+ if (!kind || kind === 'Governor') {
40
+ for (const kindOpts of generateGovernorOptions()) {
41
+ yield { kind: 'Governor', ...kindOpts };
42
+ }
33
43
  }
34
44
 
35
- for (const kindOpts of generateCustomOptions()) {
36
- yield { kind: 'Custom', ...kindOpts };
45
+ if (!kind || kind === 'Custom') {
46
+ for (const kindOpts of generateCustomOptions()) {
47
+ yield { kind: 'Custom', ...kindOpts };
48
+ }
37
49
  }
38
50
  }
39
51
 
@@ -47,10 +59,10 @@ interface GeneratedSource extends GeneratedContract {
47
59
  source: string;
48
60
  }
49
61
 
50
- function generateContractSubset(subset: Subset): GeneratedContract[] {
62
+ function generateContractSubset(subset: Subset, kind?: Kind): GeneratedContract[] {
51
63
  const contracts = [];
52
64
 
53
- for (const options of generateOptions()) {
65
+ for (const options of generateOptions(kind)) {
54
66
  const id = crypto
55
67
  .createHash('sha1')
56
68
  .update(JSON.stringify(options))
@@ -80,17 +92,17 @@ function generateContractSubset(subset: Subset): GeneratedContract[] {
80
92
  }
81
93
  }
82
94
 
83
- export function* generateSources(subset: Subset): Generator<GeneratedSource> {
84
- for (const c of generateContractSubset(subset)) {
95
+ export function* generateSources(subset: Subset, kind?: Kind): Generator<GeneratedSource> {
96
+ for (const c of generateContractSubset(subset, kind)) {
85
97
  const source = printContract(c.contract);
86
98
  yield { ...c, source };
87
99
  }
88
100
  }
89
101
 
90
- export async function writeGeneratedSources(dir: string, subset: Subset): Promise<void> {
102
+ export async function writeGeneratedSources(dir: string, subset: Subset, kind?: Kind): Promise<void> {
91
103
  await fs.mkdir(dir, { recursive: true });
92
104
 
93
- for (const { id, source } of generateSources(subset)) {
105
+ for (const { id, source } of generateSources(subset, kind)) {
94
106
  await fs.writeFile(path.format({ dir, name: id, ext: '.sol' }), source);
95
107
  }
96
108
  }
package/src/governor.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { supportsInterface } from "./common-functions";
2
2
  import { CommonOptions, withCommonDefaults, defaults as commonDefaults } from "./common-options";
3
- import { ContractBuilder, Contract } from "./contract";
3
+ import { ContractBuilder, Contract, Value } from "./contract";
4
4
  import { OptionsError } from "./error";
5
5
  import { setAccessControl } from "./set-access-control";
6
6
  import { printContract } from "./print";
7
7
  import { setInfo } from "./set-info";
8
8
  import { setUpgradeable } from "./set-upgradeable";
9
9
  import { defineFunctions } from './utils/define-functions';
10
- import { durationToBlocks } from "./utils/duration";
10
+ import { durationToBlocks, durationToTimestamp } from "./utils/duration";
11
+ import { clockModeDefault, type ClockMode } from "./set-clock-mode";
11
12
 
12
13
  export const defaults: Required<GovernorOptions> = {
13
14
  name: 'MyGovernor',
@@ -15,6 +16,7 @@ export const defaults: Required<GovernorOptions> = {
15
16
  period: '1 week',
16
17
 
17
18
  votes: 'erc20votes',
19
+ clockMode: clockModeDefault,
18
20
  timelock: 'openzeppelin',
19
21
  blockTime: 12,
20
22
  decimals: 18,
@@ -51,6 +53,7 @@ export interface GovernorOptions extends CommonOptions {
51
53
  quorumPercent?: number;
52
54
  quorumAbsolute?: string;
53
55
  votes?: VotesOptions;
56
+ clockMode?: ClockMode;
54
57
  timelock?: TimelockOptions;
55
58
  storage?: boolean;
56
59
  settings?: boolean;
@@ -73,6 +76,7 @@ function withDefaults(opts: GovernorOptions): Required<GovernorOptions> {
73
76
  storage: opts.storage ?? defaults.storage,
74
77
  quorumMode: opts.quorumMode ?? defaults.quorumMode,
75
78
  votes: opts.votes ?? defaults.votes,
79
+ clockMode: opts.clockMode ?? defaults.clockMode,
76
80
  timelock: opts.timelock ?? defaults.timelock
77
81
  };
78
82
  }
@@ -129,8 +133,8 @@ function addSettings(c: ContractBuilder, allOpts: Required<GovernorOptions>) {
129
133
  c.addParent(
130
134
  GovernorSettings,
131
135
  [
132
- { value: getVotingDelay(allOpts), note: allOpts.delay },
133
- { value: getVotingPeriod(allOpts), note: allOpts.period },
136
+ getVotingDelay(allOpts),
137
+ getVotingPeriod(allOpts),
134
138
  { lit: getProposalThreshold(allOpts) },
135
139
  ],
136
140
  );
@@ -143,9 +147,18 @@ function addSettings(c: ContractBuilder, allOpts: Required<GovernorOptions>) {
143
147
  }
144
148
  }
145
149
 
146
- function getVotingDelay(opts: Required<GovernorOptions>): number {
150
+ function getVotingDelay(opts: Required<GovernorOptions>): { lit: string } | { note: string, value: number } {
147
151
  try {
148
- return durationToBlocks(opts.delay, opts.blockTime);
152
+ if (opts.clockMode === 'timestamp') {
153
+ return {
154
+ lit: durationToTimestamp(opts.delay),
155
+ };
156
+ } else {
157
+ return {
158
+ value: durationToBlocks(opts.delay, opts.blockTime),
159
+ note: opts.delay
160
+ };
161
+ }
149
162
  } catch (e) {
150
163
  if (e instanceof Error) {
151
164
  throw new OptionsError({
@@ -157,9 +170,18 @@ function getVotingDelay(opts: Required<GovernorOptions>): number {
157
170
  }
158
171
  }
159
172
 
160
- function getVotingPeriod(opts: Required<GovernorOptions>): number {
173
+ function getVotingPeriod(opts: Required<GovernorOptions>): { lit: string } | { note: string, value: number } {
161
174
  try {
162
- return durationToBlocks(opts.period, opts.blockTime);
175
+ if (opts.clockMode === 'timestamp') {
176
+ return {
177
+ lit: durationToTimestamp(opts.period),
178
+ };
179
+ } else {
180
+ return {
181
+ value: durationToBlocks(opts.period, opts.blockTime),
182
+ note: opts.period
183
+ };
184
+ }
163
185
  } catch (e) {
164
186
  if (e instanceof Error) {
165
187
  throw new OptionsError({
@@ -195,10 +217,18 @@ function getProposalThreshold({ proposalThreshold, decimals, votes }: Required<G
195
217
 
196
218
  function setVotingParameters(c: ContractBuilder, opts: Required<GovernorOptions>) {
197
219
  const delayBlocks = getVotingDelay(opts);
198
- c.setFunctionBody([`return ${delayBlocks}; // ${opts.delay}`], functions.votingDelay);
220
+ if ('lit' in delayBlocks) {
221
+ c.setFunctionBody([`return ${delayBlocks.lit};`], functions.votingDelay);
222
+ } else {
223
+ c.setFunctionBody([`return ${delayBlocks.value}; // ${delayBlocks.note}`], functions.votingDelay);
224
+ }
199
225
 
200
226
  const periodBlocks = getVotingPeriod(opts);
201
- c.setFunctionBody([`return ${periodBlocks}; // ${opts.period}`], functions.votingPeriod);
227
+ if ('lit' in periodBlocks) {
228
+ c.setFunctionBody([`return ${periodBlocks.lit};`], functions.votingPeriod);
229
+ } else {
230
+ c.setFunctionBody([`return ${periodBlocks.value}; // ${periodBlocks.note}`], functions.votingPeriod);
231
+ }
202
232
  }
203
233
 
204
234
  function setProposalThreshold(c: ContractBuilder, opts: Required<GovernorOptions>) {
package/src/index.ts CHANGED
@@ -19,4 +19,6 @@ export { OptionsError } from './error';
19
19
  export type { Kind } from './kind';
20
20
  export { sanitizeKind } from './kind';
21
21
 
22
- export { erc20, erc721, erc1155, governor, custom } from './api';
22
+ export { erc20, erc721, erc1155, governor, custom } from './api';
23
+
24
+ export { compatibleContractsSemver } from './utils/version';
package/src/print.ts CHANGED
@@ -7,6 +7,7 @@ import { formatLines, spaceBetween, Lines } from './utils/format-lines';
7
7
  import { mapValues } from './utils/map-values';
8
8
  import SOLIDITY_VERSION from './solidity-version.json';
9
9
  import { inferTranspiled } from './infer-transpiled';
10
+ import { compatibleContractsSemver } from './utils/version';
10
11
 
11
12
  export function printContract(contract: Contract, opts?: Options): string {
12
13
  const helpers = withHelpers(contract, opts);
@@ -22,6 +23,7 @@ export function printContract(contract: Contract, opts?: Options): string {
22
23
  ...spaceBetween(
23
24
  [
24
25
  `// SPDX-License-Identifier: ${contract.license}`,
26
+ `// Compatible with OpenZeppelin Contracts ${compatibleContractsSemver}`,
25
27
  `pragma solidity ^${SOLIDITY_VERSION};`,
26
28
  ],
27
29
 
@@ -71,6 +73,7 @@ function printConstructor(contract: Contract, helpers: Helpers): Lines[] {
71
73
  : contract.constructorCode;
72
74
  const head = helpers.upgradeable ? 'function initialize' : 'constructor';
73
75
  const constructor = printFunction2(
76
+ [],
74
77
  head,
75
78
  args,
76
79
  modifiers,
@@ -191,6 +194,7 @@ function printFunction(fn: ContractFunction, helpers: Helpers): Lines[] {
191
194
 
192
195
  if (modifiers.length + fn.code.length > 1) {
193
196
  return printFunction2(
197
+ fn.comments,
194
198
  'function ' + fn.name,
195
199
  fn.args.map(a => printArgument(a, helpers)),
196
200
  modifiers,
@@ -203,8 +207,8 @@ function printFunction(fn: ContractFunction, helpers: Helpers): Lines[] {
203
207
 
204
208
  // generic for functions and constructors
205
209
  // kindedName = 'function foo' or 'constructor'
206
- function printFunction2(kindedName: string, args: string[], modifiers: string[], code: Lines[]): Lines[] {
207
- const fn = [];
210
+ function printFunction2(comments: string[], kindedName: string, args: string[], modifiers: string[], code: Lines[]): Lines[] {
211
+ const fn: Lines[] = [ ...comments ];
208
212
 
209
213
  const headingLength = [kindedName, ...args, ...modifiers]
210
214
  .map(s => s.length)
@@ -0,0 +1,33 @@
1
+ import type { ContractBuilder, ReferencedContract } from "./contract";
2
+ import { defineFunctions } from "./utils/define-functions";
3
+
4
+ export const clockModeOptions = ['blocknumber', 'timestamp'] as const;
5
+ export const clockModeDefault = 'blocknumber' as const;
6
+ export type ClockMode = typeof clockModeOptions[number];
7
+
8
+ const functions = defineFunctions({
9
+ clock: {
10
+ kind: 'public' as const,
11
+ args: [],
12
+ returns: ['uint48'],
13
+ mutability: 'view' as const,
14
+ },
15
+
16
+ CLOCK_MODE: {
17
+ kind: 'public' as const,
18
+ args: [],
19
+ returns: ['string memory'],
20
+ mutability: 'pure' as const,
21
+ }
22
+ });
23
+
24
+ export function setClockMode(c: ContractBuilder, parent: ReferencedContract, votes: ClockMode) {
25
+ if (votes === 'timestamp') {
26
+ c.addOverride(parent, functions.clock);
27
+ c.setFunctionBody(['return uint48(block.timestamp);'], functions.clock);
28
+
29
+ c.setFunctionComments(['// solhint-disable-next-line func-name-mixedcase'], functions.CLOCK_MODE);
30
+ c.addOverride(parent, functions.CLOCK_MODE);
31
+ c.setFunctionBody(['return "mode=timestamp";'], functions.CLOCK_MODE);
32
+ }
33
+ }
package/src/test.ts CHANGED
@@ -1,14 +1,42 @@
1
- import test from 'ava';
1
+ import { promises as fs } from 'fs';
2
+ import _test, { TestFn, ExecutionContext } from 'ava';
2
3
  import hre from 'hardhat';
3
4
  import path from 'path';
4
5
 
5
6
  import { generateSources, writeGeneratedSources } from './generate/sources';
6
- import type { GenericOptions } from './build-generic';
7
+ import type { GenericOptions, KindedOptions } from './build-generic';
7
8
  import { custom, erc1155, erc20, erc721, governor } from './api';
8
9
 
9
- test('result compiles', async t => {
10
- const generatedSourcesPath = path.join(hre.config.paths.sources, 'generated');
11
- await writeGeneratedSources(generatedSourcesPath, 'all');
10
+ interface Context {
11
+ generatedSourcesPath: string
12
+ }
13
+
14
+ const test = _test as TestFn<Context>;
15
+
16
+ test.serial('erc20 result compiles', async t => {
17
+ await testCompile(t, 'ERC20');
18
+ });
19
+
20
+ test.serial('erc721 result compiles', async t => {
21
+ await testCompile(t, 'ERC721');
22
+ });
23
+
24
+ test.serial('erc1155 result compiles', async t => {
25
+ await testCompile(t, 'ERC1155');
26
+ });
27
+
28
+ test.serial('governor result compiles', async t => {
29
+ await testCompile(t, 'Governor');
30
+ });
31
+
32
+ test.serial('custom result compiles', async t => {
33
+ await testCompile(t, 'Custom');
34
+ });
35
+
36
+ async function testCompile(t: ExecutionContext<Context>, kind: keyof KindedOptions) {
37
+ const generatedSourcesPath = path.join(hre.config.paths.sources, `generated`);
38
+ await fs.rm(generatedSourcesPath, { force: true, recursive: true });
39
+ await writeGeneratedSources(generatedSourcesPath, 'all', kind);
12
40
 
13
41
  // We only want to check that contracts compile and we don't care about any
14
42
  // of the outputs. Setting empty outputSelection causes compilation to go a
@@ -19,7 +47,7 @@ test('result compiles', async t => {
19
47
 
20
48
  await hre.run('compile');
21
49
  t.pass();
22
- });
50
+ }
23
51
 
24
52
  function isAccessControlRequired(opts: GenericOptions) {
25
53
  switch(opts.kind) {
@@ -32,3 +32,21 @@ export function durationToBlocks(duration: string, blockTime: number): number {
32
32
  const durationSeconds = value * secondsForUnit[unit];
33
33
  return Math.round(durationSeconds / blockTime);
34
34
  }
35
+
36
+ export function durationToTimestamp(duration: string): string {
37
+ const match = duration.trim().match(durationPattern);
38
+
39
+ if (!match) {
40
+ throw new Error('Bad duration format');
41
+ }
42
+
43
+ const value = match[1]!;
44
+ const unit = match[2]! as DurationUnit;
45
+
46
+ // unit must be a Solidity supported time unit
47
+ if (unit === 'block' || unit === 'month' || unit === 'year') {
48
+ throw new Error('Invalid unit for timestamp');
49
+ }
50
+
51
+ return `${value} ${unit}s`;
52
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Semantic version string representing of the minimum compatible version of Contracts to display in output.
3
+ */
4
+ export const compatibleContractsSemver = '^5.0.0';