@openzeppelin/wizard 0.1.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 (235) hide show
  1. package/README.md +82 -0
  2. package/dist/add-pausable.d.ts +4 -0
  3. package/dist/add-pausable.d.ts.map +1 -0
  4. package/dist/add-pausable.js +30 -0
  5. package/dist/add-pausable.js.map +1 -0
  6. package/dist/api.d.ts +24 -0
  7. package/dist/api.d.ts.map +1 -0
  8. package/dist/api.js +24 -0
  9. package/dist/api.js.map +1 -0
  10. package/dist/build-generic.d.ts +21 -0
  11. package/dist/build-generic.d.ts.map +1 -0
  12. package/dist/build-generic.js +24 -0
  13. package/dist/build-generic.js.map +1 -0
  14. package/dist/common-functions.d.ts +3 -0
  15. package/dist/common-functions.d.ts.map +1 -0
  16. package/dist/common-functions.js +13 -0
  17. package/dist/common-functions.js.map +1 -0
  18. package/dist/common-options.d.ts +11 -0
  19. package/dist/common-options.d.ts.map +1 -0
  20. package/dist/common-options.js +19 -0
  21. package/dist/common-options.js.map +1 -0
  22. package/dist/contract.d.ts +86 -0
  23. package/dist/contract.d.ts.map +1 -0
  24. package/dist/contract.js +125 -0
  25. package/dist/contract.js.map +1 -0
  26. package/dist/contract.test.d.ts +2 -0
  27. package/dist/contract.test.d.ts.map +1 -0
  28. package/dist/contract.test.js +147 -0
  29. package/dist/contract.test.js.map +1 -0
  30. package/dist/erc1155.d.ts +14 -0
  31. package/dist/erc1155.d.ts.map +1 -0
  32. package/dist/erc1155.js +130 -0
  33. package/dist/erc1155.js.map +1 -0
  34. package/dist/erc1155.test.d.ts +2 -0
  35. package/dist/erc1155.test.d.ts.map +1 -0
  36. package/dist/erc1155.test.js +80 -0
  37. package/dist/erc1155.test.js.map +1 -0
  38. package/dist/erc20.d.ts +19 -0
  39. package/dist/erc20.d.ts.map +1 -0
  40. package/dist/erc20.js +202 -0
  41. package/dist/erc20.js.map +1 -0
  42. package/dist/erc20.test.d.ts +2 -0
  43. package/dist/erc20.test.d.ts.map +1 -0
  44. package/dist/erc20.test.js +126 -0
  45. package/dist/erc20.test.js.map +1 -0
  46. package/dist/erc721.d.ts +18 -0
  47. package/dist/erc721.d.ts.map +1 -0
  48. package/dist/erc721.js +200 -0
  49. package/dist/erc721.js.map +1 -0
  50. package/dist/erc721.test.d.ts +2 -0
  51. package/dist/erc721.test.d.ts.map +1 -0
  52. package/dist/erc721.test.js +106 -0
  53. package/dist/erc721.test.js.map +1 -0
  54. package/dist/error.d.ts +8 -0
  55. package/dist/error.d.ts.map +1 -0
  56. package/dist/error.js +11 -0
  57. package/dist/error.js.map +1 -0
  58. package/dist/general.d.ts +8 -0
  59. package/dist/general.d.ts.map +1 -0
  60. package/dist/general.js +22 -0
  61. package/dist/general.js.map +1 -0
  62. package/dist/general.test.d.ts +2 -0
  63. package/dist/general.test.d.ts.map +1 -0
  64. package/dist/general.test.js +42 -0
  65. package/dist/general.test.js.map +1 -0
  66. package/dist/generate/alternatives.d.ts +7 -0
  67. package/dist/generate/alternatives.d.ts.map +1 -0
  68. package/dist/generate/alternatives.js +29 -0
  69. package/dist/generate/alternatives.js.map +1 -0
  70. package/dist/generate/erc1155.d.ts +3 -0
  71. package/dist/generate/erc1155.d.ts.map +1 -0
  72. package/dist/generate/erc1155.js +24 -0
  73. package/dist/generate/erc1155.js.map +1 -0
  74. package/dist/generate/erc20.d.ts +3 -0
  75. package/dist/generate/erc20.d.ts.map +1 -0
  76. package/dist/generate/erc20.js +28 -0
  77. package/dist/generate/erc20.js.map +1 -0
  78. package/dist/generate/erc721.d.ts +3 -0
  79. package/dist/generate/erc721.d.ts.map +1 -0
  80. package/dist/generate/erc721.js +28 -0
  81. package/dist/generate/erc721.js.map +1 -0
  82. package/dist/generate/governor.d.ts +3 -0
  83. package/dist/generate/governor.d.ts.map +1 -0
  84. package/dist/generate/governor.js +32 -0
  85. package/dist/generate/governor.js.map +1 -0
  86. package/dist/generate/sources.d.ts +16 -0
  87. package/dist/generate/sources.d.ts.map +1 -0
  88. package/dist/generate/sources.js +79 -0
  89. package/dist/generate/sources.js.map +1 -0
  90. package/dist/governor.d.ts +26 -0
  91. package/dist/governor.d.ts.map +1 -0
  92. package/dist/governor.js +386 -0
  93. package/dist/governor.js.map +1 -0
  94. package/dist/governor.test.d.ts +2 -0
  95. package/dist/governor.test.d.ts.map +1 -0
  96. package/dist/governor.test.js +104 -0
  97. package/dist/governor.test.js.map +1 -0
  98. package/dist/index.d.ts +17 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +25 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/kind.d.ts +4 -0
  103. package/dist/kind.d.ts.map +1 -0
  104. package/dist/kind.js +28 -0
  105. package/dist/kind.js.map +1 -0
  106. package/dist/options.d.ts +11 -0
  107. package/dist/options.d.ts.map +1 -0
  108. package/dist/options.js +40 -0
  109. package/dist/options.js.map +1 -0
  110. package/dist/print-versioned.d.ts +3 -0
  111. package/dist/print-versioned.d.ts.map +1 -0
  112. package/dist/print-versioned.js +12 -0
  113. package/dist/print-versioned.js.map +1 -0
  114. package/dist/print.d.ts +6 -0
  115. package/dist/print.d.ts.map +1 -0
  116. package/dist/print.js +182 -0
  117. package/dist/print.js.map +1 -0
  118. package/dist/scripts/prepare.d.ts +2 -0
  119. package/dist/scripts/prepare.d.ts.map +1 -0
  120. package/dist/scripts/prepare.js +52 -0
  121. package/dist/scripts/prepare.js.map +1 -0
  122. package/dist/set-access-control.d.ts +5 -0
  123. package/dist/set-access-control.d.ts.map +1 -0
  124. package/dist/set-access-control.js +38 -0
  125. package/dist/set-access-control.js.map +1 -0
  126. package/dist/set-info.d.ts +13 -0
  127. package/dist/set-info.d.ts.map +1 -0
  128. package/dist/set-info.js +17 -0
  129. package/dist/set-info.js.map +1 -0
  130. package/dist/set-upgradeable.d.ts +6 -0
  131. package/dist/set-upgradeable.d.ts.map +1 -0
  132. package/dist/set-upgradeable.js +43 -0
  133. package/dist/set-upgradeable.js.map +1 -0
  134. package/dist/test.d.ts +2 -0
  135. package/dist/test.d.ts.map +1 -0
  136. package/dist/test.js +23 -0
  137. package/dist/test.js.map +1 -0
  138. package/dist/utils/define-functions.d.ts +5 -0
  139. package/dist/utils/define-functions.d.ts.map +1 -0
  140. package/dist/utils/define-functions.js +11 -0
  141. package/dist/utils/define-functions.js.map +1 -0
  142. package/dist/utils/duration.d.ts +3 -0
  143. package/dist/utils/duration.d.ts.map +1 -0
  144. package/dist/utils/duration.js +31 -0
  145. package/dist/utils/duration.js.map +1 -0
  146. package/dist/utils/find-cover.d.ts +2 -0
  147. package/dist/utils/find-cover.d.ts.map +1 -0
  148. package/dist/utils/find-cover.js +23 -0
  149. package/dist/utils/find-cover.js.map +1 -0
  150. package/dist/utils/format-lines.d.ts +7 -0
  151. package/dist/utils/format-lines.d.ts.map +1 -0
  152. package/dist/utils/format-lines.js +30 -0
  153. package/dist/utils/format-lines.js.map +1 -0
  154. package/dist/utils/map-values.d.ts +2 -0
  155. package/dist/utils/map-values.d.ts.map +1 -0
  156. package/dist/utils/map-values.js +12 -0
  157. package/dist/utils/map-values.js.map +1 -0
  158. package/dist/utils/sorted-by.d.ts +2 -0
  159. package/dist/utils/sorted-by.d.ts.map +1 -0
  160. package/dist/utils/sorted-by.js +8 -0
  161. package/dist/utils/sorted-by.js.map +1 -0
  162. package/dist/utils/to-identifier.d.ts +2 -0
  163. package/dist/utils/to-identifier.d.ts.map +1 -0
  164. package/dist/utils/to-identifier.js +12 -0
  165. package/dist/utils/to-identifier.js.map +1 -0
  166. package/dist/utils/to-identifier.test.d.ts +2 -0
  167. package/dist/utils/to-identifier.test.d.ts.map +1 -0
  168. package/dist/utils/to-identifier.test.js +21 -0
  169. package/dist/utils/to-identifier.test.js.map +1 -0
  170. package/dist/utils/transitive-closure.d.ts +5 -0
  171. package/dist/utils/transitive-closure.d.ts.map +1 -0
  172. package/dist/utils/transitive-closure.js +27 -0
  173. package/dist/utils/transitive-closure.js.map +1 -0
  174. package/dist/zip.d.ts +4 -0
  175. package/dist/zip.d.ts.map +1 -0
  176. package/dist/zip.js +48 -0
  177. package/dist/zip.js.map +1 -0
  178. package/dist/zip.test.d.ts +2 -0
  179. package/dist/zip.test.d.ts.map +1 -0
  180. package/dist/zip.test.js +37 -0
  181. package/dist/zip.test.js.map +1 -0
  182. package/package.json +37 -0
  183. package/src/add-pausable.ts +32 -0
  184. package/src/api.ts +39 -0
  185. package/src/build-generic.ts +33 -0
  186. package/src/common-functions.ts +11 -0
  187. package/src/common-options.ts +24 -0
  188. package/src/contract.test.ts +164 -0
  189. package/src/contract.test.ts.md +272 -0
  190. package/src/contract.test.ts.snap +0 -0
  191. package/src/contract.ts +201 -0
  192. package/src/erc1155.test.ts +90 -0
  193. package/src/erc1155.test.ts.md +416 -0
  194. package/src/erc1155.test.ts.snap +0 -0
  195. package/src/erc1155.ts +160 -0
  196. package/src/erc20.test.ts +144 -0
  197. package/src/erc20.test.ts.md +571 -0
  198. package/src/erc20.test.ts.snap +0 -0
  199. package/src/erc20.ts +250 -0
  200. package/src/erc721.test.ts +122 -0
  201. package/src/erc721.test.ts.md +517 -0
  202. package/src/erc721.test.ts.snap +0 -0
  203. package/src/erc721.ts +250 -0
  204. package/src/error.ts +7 -0
  205. package/src/generate/alternatives.ts +38 -0
  206. package/src/generate/erc1155.ts +23 -0
  207. package/src/generate/erc20.ts +27 -0
  208. package/src/generate/erc721.ts +27 -0
  209. package/src/generate/governor.ts +30 -0
  210. package/src/generate/sources.ts +91 -0
  211. package/src/governor.test.ts +120 -0
  212. package/src/governor.test.ts.md +1419 -0
  213. package/src/governor.test.ts.snap +0 -0
  214. package/src/governor.ts +445 -0
  215. package/src/index.ts +23 -0
  216. package/src/kind.ts +30 -0
  217. package/src/options.ts +45 -0
  218. package/src/print-versioned.ts +12 -0
  219. package/src/print.ts +236 -0
  220. package/src/scripts/prepare.ts +60 -0
  221. package/src/set-access-control.ts +39 -0
  222. package/src/set-info.ts +24 -0
  223. package/src/set-upgradeable.ts +49 -0
  224. package/src/test.ts +21 -0
  225. package/src/utils/define-functions.ts +18 -0
  226. package/src/utils/duration.ts +34 -0
  227. package/src/utils/find-cover.ts +26 -0
  228. package/src/utils/format-lines.ts +31 -0
  229. package/src/utils/map-values.ts +10 -0
  230. package/src/utils/sorted-by.ts +3 -0
  231. package/src/utils/to-identifier.test.ts +20 -0
  232. package/src/utils/to-identifier.ts +7 -0
  233. package/src/utils/transitive-closure.ts +27 -0
  234. package/src/zip.test.ts +35 -0
  235. package/src/zip.ts +53 -0
Binary file
@@ -0,0 +1,445 @@
1
+ import { supportsInterface } from "./common-functions";
2
+ import { CommonOptions, withCommonDefaults, defaults as commonDefaults } from "./common-options";
3
+ import { ContractBuilder, Contract } from "./contract";
4
+ import { OptionsError } from "./error";
5
+ import { printContract } from "./print";
6
+ import { setInfo } from "./set-info";
7
+ import { setUpgradeable } from "./set-upgradeable";
8
+ import { defineFunctions } from './utils/define-functions';
9
+ import { durationToBlocks } from "./utils/duration";
10
+
11
+ export const defaults: Required<GovernorOptions> = {
12
+ name: 'MyGovernor',
13
+ delay: '1 block',
14
+ period: '1 week',
15
+
16
+ votes: 'erc20votes',
17
+ timelock: 'openzeppelin',
18
+ blockTime: 13.2,
19
+ decimals: 18,
20
+ proposalThreshold: '0',
21
+ quorumMode: 'percent',
22
+ quorumPercent: 4,
23
+ quorumAbsolute: '',
24
+ bravo: false,
25
+ settings: true,
26
+
27
+ access: commonDefaults.access,
28
+ upgradeable: commonDefaults.upgradeable,
29
+ info: commonDefaults.info
30
+ } as const;
31
+
32
+ export const votesOptions = ['erc20votes', 'erc721votes', 'comp'] as const;
33
+ export type VotesOptions = typeof votesOptions[number];
34
+
35
+ export const timelockOptions = [false, 'openzeppelin', 'compound'] as const;
36
+ export type TimelockOptions = typeof timelockOptions[number];
37
+
38
+ export function printGovernor(opts: GovernorOptions = defaults): string {
39
+ return printContract(buildGovernor(opts));
40
+ }
41
+
42
+ export interface GovernorOptions extends CommonOptions {
43
+ name: string;
44
+ delay: string;
45
+ period: string;
46
+ blockTime?: number;
47
+ proposalThreshold?: string;
48
+ decimals?: number;
49
+ quorumMode?: 'percent' | 'absolute';
50
+ quorumPercent?: number;
51
+ quorumAbsolute?: string;
52
+ votes?: VotesOptions;
53
+ timelock?: TimelockOptions;
54
+ bravo?: boolean;
55
+ settings?: boolean;
56
+ }
57
+
58
+ function withDefaults(opts: GovernorOptions): Required<GovernorOptions> {
59
+ return {
60
+ ...opts,
61
+ ...withCommonDefaults(opts),
62
+ decimals: opts.decimals ?? defaults.decimals,
63
+ blockTime: opts.blockTime || defaults.blockTime,
64
+ quorumPercent: opts.quorumPercent ?? defaults.quorumPercent,
65
+ quorumAbsolute: opts.quorumAbsolute ?? defaults.quorumAbsolute,
66
+ proposalThreshold: opts.proposalThreshold || defaults.proposalThreshold,
67
+ settings: opts.settings ?? defaults.settings,
68
+ bravo: opts.bravo ?? defaults.bravo,
69
+ quorumMode: opts.quorumMode ?? defaults.quorumMode,
70
+ votes: opts.votes ?? defaults.votes,
71
+ timelock: opts.timelock ?? defaults.timelock
72
+ };
73
+ }
74
+
75
+ export function buildGovernor(opts: GovernorOptions): Contract {
76
+ const allOpts = withDefaults(opts);
77
+
78
+ const c = new ContractBuilder(allOpts.name);
79
+
80
+ validateDecimals(allOpts.decimals);
81
+
82
+ addBase(c, allOpts);
83
+ addSettings(c, allOpts);
84
+ addCounting(c, allOpts);
85
+ addBravo(c, allOpts);
86
+ addVotes(c, allOpts);
87
+ addQuorum(c, allOpts);
88
+ addTimelock(c, allOpts);
89
+
90
+ setUpgradeable(c, allOpts.upgradeable, allOpts.access);
91
+
92
+ setInfo(c, allOpts.info);
93
+
94
+ return c;
95
+ }
96
+
97
+ function addBase(c: ContractBuilder, { name }: GovernorOptions) {
98
+ c.addParent(
99
+ {
100
+ name: 'Governor',
101
+ path: '@openzeppelin/contracts/governance/Governor.sol',
102
+ },
103
+ [name],
104
+ );
105
+ c.addOverride('IGovernor', functions.votingDelay);
106
+ c.addOverride('IGovernor', functions.votingPeriod);
107
+ c.addOverride('IGovernor', functions.quorum);
108
+ c.addOverride('Governor', functions.state);
109
+ c.addOverride('Governor', functions.propose);
110
+ c.addOverride('Governor', functions.proposalThreshold);
111
+ c.addOverride('Governor', functions._execute);
112
+ c.addOverride('Governor', functions._cancel);
113
+ c.addOverride('Governor', functions._executor);
114
+ c.addOverride('Governor', supportsInterface);
115
+ }
116
+
117
+ function addSettings(c: ContractBuilder, allOpts: Required<GovernorOptions>) {
118
+ if (allOpts.settings) {
119
+ c.addParent(
120
+ {
121
+ name: 'GovernorSettings',
122
+ path: '@openzeppelin/contracts/governance/extensions/GovernorSettings.sol',
123
+ },
124
+ [
125
+ { value: getVotingDelay(allOpts), note: allOpts.delay },
126
+ { value: getVotingPeriod(allOpts), note: allOpts.period },
127
+ { lit: getProposalThreshold(allOpts) },
128
+ ],
129
+ );
130
+ c.addOverride('GovernorSettings', functions.votingDelay, 'view');
131
+ c.addOverride('GovernorSettings', functions.votingPeriod, 'view');
132
+ c.addOverride('GovernorSettings', functions.proposalThreshold, 'view');
133
+ } else {
134
+ setVotingParameters(c, allOpts);
135
+ setProposalThreshold(c, allOpts);
136
+ }
137
+ }
138
+
139
+ function getVotingDelay(opts: Required<GovernorOptions>): number {
140
+ try {
141
+ return durationToBlocks(opts.delay, opts.blockTime);
142
+ } catch (e) {
143
+ if (e instanceof Error) {
144
+ throw new OptionsError({
145
+ delay: e.message,
146
+ });
147
+ } else {
148
+ throw e;
149
+ }
150
+ }
151
+ }
152
+
153
+ function getVotingPeriod(opts: Required<GovernorOptions>): number {
154
+ try {
155
+ return durationToBlocks(opts.period, opts.blockTime);
156
+ } catch (e) {
157
+ if (e instanceof Error) {
158
+ throw new OptionsError({
159
+ period: e.message,
160
+ });
161
+ } else {
162
+ throw e;
163
+ }
164
+ }
165
+ }
166
+
167
+ function validateDecimals(decimals: number) {
168
+ if (!/^\d+$/.test(decimals.toString())) {
169
+ throw new OptionsError({
170
+ decimals: 'Not a valid number',
171
+ });
172
+ }
173
+ }
174
+
175
+ function getProposalThreshold({ proposalThreshold, decimals, votes }: Required<GovernorOptions>): string {
176
+ if (!/^\d+$/.test(proposalThreshold)) {
177
+ throw new OptionsError({
178
+ proposalThreshold: 'Not a valid number',
179
+ });
180
+ }
181
+
182
+ if (/^0+$/.test(proposalThreshold) || decimals === 0 || votes === 'erc721votes') {
183
+ return proposalThreshold;
184
+ } else {
185
+ return `${proposalThreshold}e${decimals}`;
186
+ }
187
+ }
188
+
189
+ function setVotingParameters(c: ContractBuilder, opts: Required<GovernorOptions>) {
190
+ const delayBlocks = getVotingDelay(opts);
191
+ c.setFunctionBody([`return ${delayBlocks}; // ${opts.delay}`], functions.votingDelay);
192
+
193
+ const periodBlocks = getVotingPeriod(opts);
194
+ c.setFunctionBody([`return ${periodBlocks}; // ${opts.period}`], functions.votingPeriod);
195
+ }
196
+
197
+ function setProposalThreshold(c: ContractBuilder, opts: Required<GovernorOptions>) {
198
+ const threshold = getProposalThreshold(opts);
199
+ const nonZeroThreshold = parseInt(threshold) !== 0;
200
+
201
+ if (nonZeroThreshold) {
202
+ c.setFunctionBody([`return ${threshold};`], functions.proposalThreshold);
203
+ }
204
+ }
205
+
206
+ function addCounting(c: ContractBuilder, { bravo }: GovernorOptions) {
207
+ if (!bravo) {
208
+ c.addParent({
209
+ name: 'GovernorCountingSimple',
210
+ path: '@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol',
211
+ });
212
+ }
213
+ }
214
+
215
+ const votesModules = {
216
+ erc20votes: {
217
+ tokenType: 'IVotes',
218
+ parentName: 'GovernorVotes',
219
+ },
220
+ erc721votes: {
221
+ tokenType: 'IVotes',
222
+ parentName: 'GovernorVotes',
223
+ },
224
+ comp: {
225
+ tokenType: 'ERC20VotesComp',
226
+ parentName: 'GovernorVotesComp',
227
+ },
228
+ } as const;
229
+
230
+ function addVotes(c: ContractBuilder, { votes }: Required<GovernorOptions>) {
231
+ const tokenArg = '_token';
232
+ const { tokenType, parentName } = votesModules[votes];
233
+
234
+ c.addConstructorArgument({
235
+ type: tokenType,
236
+ name: tokenArg,
237
+ });
238
+
239
+ c.addParent({
240
+ name: parentName,
241
+ path: `@openzeppelin/contracts/governance/extensions/${parentName}.sol`,
242
+ }, [{ lit: tokenArg }]);
243
+ }
244
+
245
+ export const numberPattern = /^(?!$)(\d*)(?:\.(\d+))?(?:e(\d+))?$/;
246
+
247
+ function addQuorum(c: ContractBuilder, opts: Required<GovernorOptions>) {
248
+ if (opts.quorumMode === 'percent') {
249
+ if (opts.votes !== 'erc20votes' && opts.votes !== 'erc721votes') {
250
+ throw new OptionsError({
251
+ quorumPercent: 'Percent-based quorum is only available for ERC20Votes or ERC721Votes',
252
+ });
253
+ }
254
+
255
+ if (opts.quorumPercent > 100) {
256
+ throw new OptionsError({
257
+ quorumPercent: 'Invalid percentage',
258
+ });
259
+ }
260
+
261
+ let { quorumFractionNumerator, quorumFractionDenominator } = getQuorumFractionComponents(opts.quorumPercent);
262
+
263
+ if (quorumFractionDenominator !== undefined) {
264
+ c.addOverride('GovernorVotesQuorumFraction', functions.quorumDenominator);
265
+ c.setFunctionBody([
266
+ `return ${quorumFractionDenominator};`
267
+ ], functions.quorumDenominator, 'pure');
268
+ }
269
+
270
+ c.addParent({
271
+ name: 'GovernorVotesQuorumFraction',
272
+ path: '@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol',
273
+ }, [quorumFractionNumerator]);
274
+ c.addOverride('GovernorVotesQuorumFraction', functions.quorum);
275
+ }
276
+
277
+ else if (opts.quorumMode === 'absolute') {
278
+ if (!numberPattern.test(opts.quorumAbsolute)) {
279
+ throw new OptionsError({
280
+ quorumAbsolute: 'Not a valid number',
281
+ });
282
+ }
283
+
284
+ let returnStatement = (opts.decimals === 0 || opts.votes === 'erc721votes') ?
285
+ `return ${opts.quorumAbsolute};` :
286
+ `return ${opts.quorumAbsolute}e${opts.decimals};`;
287
+
288
+ c.setFunctionBody([
289
+ returnStatement,
290
+ ], functions.quorum, 'pure');
291
+ }
292
+ }
293
+
294
+ const timelockModules = {
295
+ openzeppelin: {
296
+ timelockType: 'TimelockController',
297
+ parentName: 'GovernorTimelockControl',
298
+ },
299
+ compound: {
300
+ timelockType: 'ICompoundTimelock',
301
+ parentName: 'GovernorTimelockCompound',
302
+ },
303
+ } as const;
304
+
305
+ function getQuorumFractionComponents(quorumPercent: number): {quorumFractionNumerator: number, quorumFractionDenominator: string | undefined} {
306
+ let quorumFractionNumerator = quorumPercent;
307
+ let quorumFractionDenominator = undefined;
308
+
309
+ const quorumPercentSegments = quorumPercent.toString().split(".");
310
+ if (quorumPercentSegments.length > 2) {
311
+ throw new OptionsError({
312
+ quorumPercent: 'Invalid percentage',
313
+ });
314
+ } else if (quorumPercentSegments.length == 2 && quorumPercentSegments[0] !== undefined && quorumPercentSegments[1] !== undefined) {
315
+ quorumFractionNumerator = parseInt(quorumPercentSegments[0].concat(quorumPercentSegments[1]));
316
+ const decimals = quorumPercentSegments[1].length;
317
+ quorumFractionDenominator = '100';
318
+ while (quorumFractionDenominator.length < decimals + 3) {
319
+ quorumFractionDenominator += '0';
320
+ }
321
+ }
322
+ return { quorumFractionNumerator, quorumFractionDenominator };
323
+ }
324
+
325
+ function addTimelock(c: ContractBuilder, { timelock }: Required<GovernorOptions>) {
326
+ if (timelock === false) {
327
+ return;
328
+ }
329
+
330
+ const timelockArg = '_timelock';
331
+ const { timelockType, parentName } = timelockModules[timelock];
332
+
333
+ c.addConstructorArgument({
334
+ type: timelockType,
335
+ name: timelockArg,
336
+ });
337
+
338
+ c.addParent({
339
+ name: parentName,
340
+ path: `@openzeppelin/contracts/governance/extensions/${parentName}.sol`,
341
+ }, [{ lit: timelockArg }]);
342
+ c.addOverride('IGovernor', functions.propose);
343
+ c.addOverride(parentName, functions._execute);
344
+ c.addOverride(parentName, functions._cancel);
345
+ c.addOverride(parentName, functions._executor);
346
+ c.addOverride(parentName, functions.state);
347
+ c.addOverride(parentName, supportsInterface);
348
+ }
349
+
350
+ function addBravo(c: ContractBuilder, { bravo, timelock }: GovernorOptions) {
351
+ if (bravo) {
352
+ if (timelock === false) {
353
+ throw new OptionsError({
354
+ timelock: 'GovernorBravo compatibility requires a timelock',
355
+ });
356
+ }
357
+
358
+ c.addParent({
359
+ name: 'GovernorCompatibilityBravo',
360
+ path: '@openzeppelin/contracts/governance/compatibility/GovernorCompatibilityBravo.sol',
361
+ });
362
+ c.addOverride('IGovernor', functions.state);
363
+ c.addOverride('GovernorCompatibilityBravo', functions.propose);
364
+ c.addOverride('IERC165', supportsInterface);
365
+ }
366
+ }
367
+
368
+ const functions = defineFunctions({
369
+ votingDelay: {
370
+ args: [],
371
+ returns: ['uint256'],
372
+ kind: 'public',
373
+ mutability: 'pure',
374
+ },
375
+ votingPeriod: {
376
+ args: [],
377
+ returns: ['uint256'],
378
+ kind: 'public',
379
+ mutability: 'pure',
380
+ },
381
+ proposalThreshold: {
382
+ args: [],
383
+ returns: ['uint256'],
384
+ kind: 'public',
385
+ mutability: 'pure',
386
+ },
387
+ quorum: {
388
+ args: [
389
+ { name: 'blockNumber', type: 'uint256' },
390
+ ],
391
+ returns: ['uint256'],
392
+ kind: 'public',
393
+ mutability: 'view',
394
+ },
395
+ quorumDenominator: {
396
+ args: [],
397
+ returns: ['uint256'],
398
+ kind: 'public',
399
+ mutability: 'view',
400
+ },
401
+ propose: {
402
+ args: [
403
+ { name: 'targets', type: 'address[] memory' },
404
+ { name: 'values', type: 'uint256[] memory' },
405
+ { name: 'calldatas', type: 'bytes[] memory' },
406
+ { name: 'description', type: 'string memory' },
407
+ ],
408
+ returns: ['uint256'],
409
+ kind: 'public',
410
+ },
411
+ _execute: {
412
+ args: [
413
+ { name: 'proposalId', type: 'uint256' },
414
+ { name: 'targets', type: 'address[] memory' },
415
+ { name: 'values', type: 'uint256[] memory' },
416
+ { name: 'calldatas', type: 'bytes[] memory' },
417
+ { name: 'descriptionHash', type: 'bytes32' },
418
+ ],
419
+ kind: 'internal',
420
+ },
421
+ _cancel: {
422
+ args: [
423
+ { name: 'targets', type: 'address[] memory' },
424
+ { name: 'values', type: 'uint256[] memory' },
425
+ { name: 'calldatas', type: 'bytes[] memory' },
426
+ { name: 'descriptionHash', type: 'bytes32' },
427
+ ],
428
+ returns: ['uint256'],
429
+ kind: 'internal',
430
+ },
431
+ state: {
432
+ args: [
433
+ { name: 'proposalId', type: 'uint256' },
434
+ ],
435
+ returns: ['ProposalState'],
436
+ kind: 'public',
437
+ mutability: 'view',
438
+ },
439
+ _executor: {
440
+ args: [],
441
+ returns: ['address'],
442
+ kind: 'internal',
443
+ mutability: 'view',
444
+ },
445
+ });
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export type { GenericOptions, KindedOptions } from './build-generic';
2
+ export { buildGeneric } from './build-generic';
3
+
4
+ export type { Contract } from './contract';
5
+ export { ContractBuilder } from './contract';
6
+
7
+ export { printContract } from './print';
8
+ export { printContractVersioned } from './print-versioned';
9
+
10
+ export type { Access } from './set-access-control';
11
+ export type { Upgradeable } from './set-upgradeable';
12
+ export type { Info } from './set-info';
13
+
14
+ export { premintPattern } from './erc20';
15
+ export { defaults as infoDefaults } from './set-info';
16
+
17
+ export type { OptionsErrorMessages } from './error';
18
+ export { OptionsError } from './error';
19
+
20
+ export type { Kind } from './kind';
21
+ export { sanitizeKind } from './kind';
22
+
23
+ export { erc20, erc721, erc1155, governor } from './api';
package/src/kind.ts ADDED
@@ -0,0 +1,30 @@
1
+ import type { GenericOptions } from './build-generic';
2
+
3
+ export type Kind = GenericOptions['kind'];
4
+
5
+ export function sanitizeKind(kind: unknown): Kind {
6
+ if (typeof kind === 'string') {
7
+ const sanitized = kind.replace(/^(ERC|.)/i, c => c.toUpperCase());
8
+ if (isKind(sanitized)) {
9
+ return sanitized;
10
+ }
11
+ }
12
+ return 'ERC20';
13
+ }
14
+
15
+ function isKind<T>(value: Kind | T): value is Kind {
16
+ switch (value) {
17
+ case 'ERC20':
18
+ case 'ERC1155':
19
+ case 'ERC721':
20
+ case 'Governor':
21
+ return true;
22
+
23
+ default: {
24
+ // Static assert that we've checked all kinds.
25
+ const _: T = value;
26
+ return false;
27
+ }
28
+ }
29
+ }
30
+
package/src/options.ts ADDED
@@ -0,0 +1,45 @@
1
+ import path from 'path';
2
+
3
+ import type { Contract } from './contract';
4
+
5
+ const upgradeableName = (n: string) => {
6
+ if (n === 'Initializable') {
7
+ return n;
8
+ } else {
9
+ return n.replace(/(Upgradeable)?(?=\.|$)/, 'Upgradeable');
10
+ }
11
+ }
12
+
13
+ const upgradeableImport = (p: string) => {
14
+ const { dir, ext, name } = path.parse(p);
15
+ // Use path.posix to get forward slashes
16
+ return path.posix.format({
17
+ ext,
18
+ dir: dir.replace(/^@openzeppelin\/contracts/, '@openzeppelin/contracts-upgradeable'),
19
+ name: upgradeableName(name),
20
+ });
21
+ };
22
+
23
+ export interface Options {
24
+ transformImport?: (path: string) => string;
25
+ }
26
+
27
+ export interface Helpers extends Required<Options> {
28
+ upgradeable: boolean;
29
+ transformName: (name: string) => string;
30
+ transformVariable: (code: string) => string;
31
+ }
32
+
33
+ export function withHelpers(contract: Contract, opts: Options = {}): Helpers {
34
+ const upgradeable = contract.upgradeable;
35
+ const transformName = (n: string) => upgradeable ? upgradeableName(n) : n;
36
+ return {
37
+ upgradeable,
38
+ transformName,
39
+ transformImport: p1 => {
40
+ const p2 = upgradeable ? upgradeableImport(p1) : p1;
41
+ return opts.transformImport?.(p2) ?? p2;
42
+ },
43
+ transformVariable: v => v.replace(/[A-Z]\w*(?=\.|$)/, transformName),
44
+ };
45
+ }
@@ -0,0 +1,12 @@
1
+ import { version as contractsVersion } from "@openzeppelin/contracts/package.json";
2
+ import type { Contract } from "./contract";
3
+ import { printContract } from "./print";
4
+
5
+ export function printContractVersioned(contract: Contract): string {
6
+ return printContract(contract, {
7
+ transformImport: p =>
8
+ p.replace(/^@openzeppelin\/contracts(-upgradeable)?/, `$&@${contractsVersion}`),
9
+ });
10
+ }
11
+
12
+