@openzeppelin/wizard 0.5.3 → 0.5.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.
- package/README.md +13 -3
- package/dist/account.d.ts +20 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +252 -0
- package/dist/account.js.map +1 -0
- package/dist/api.d.ts +12 -7
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +6 -1
- package/dist/api.js.map +1 -1
- package/dist/build-generic.d.ts +4 -0
- package/dist/build-generic.d.ts.map +1 -1
- package/dist/build-generic.js +3 -0
- package/dist/build-generic.js.map +1 -1
- package/dist/environments/hardhat/package-lock.json +3 -3
- package/dist/environments/hardhat/upgradeable/package-lock.json +7 -7
- package/dist/erc20.d.ts +1 -0
- package/dist/erc20.d.ts.map +1 -1
- package/dist/erc20.js +46 -6
- package/dist/erc20.js.map +1 -1
- package/dist/generate/account.d.ts +3 -0
- package/dist/generate/account.d.ts.map +1 -0
- package/dist/generate/account.js +21 -0
- package/dist/generate/account.js.map +1 -0
- package/dist/generate/erc20.d.ts.map +1 -1
- package/dist/generate/erc20.js +1 -0
- package/dist/generate/erc20.js.map +1 -1
- package/dist/generate/sources.d.ts.map +1 -1
- package/dist/generate/sources.js +6 -0
- package/dist/generate/sources.js.map +1 -1
- package/dist/generate/stablecoin.d.ts.map +1 -1
- package/dist/generate/stablecoin.js +2 -0
- package/dist/generate/stablecoin.js.map +1 -1
- package/dist/governor.d.ts.map +1 -1
- package/dist/governor.js +7 -7
- package/dist/governor.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/kind.js +2 -1
- package/dist/kind.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +2 -1
- package/dist/print.js.map +1 -1
- package/dist/signer.d.ts +39 -0
- package/dist/signer.d.ts.map +1 -0
- package/dist/signer.js +115 -0
- package/dist/signer.js.map +1 -0
- package/dist/solidity-version.json +1 -1
- package/dist/stablecoin.js +2 -2
- package/dist/stablecoin.js.map +1 -1
- package/dist/test.js +17 -6
- package/dist/test.js.map +1 -1
- package/dist/utils/convert-strings.d.ts +11 -0
- package/dist/utils/convert-strings.d.ts.map +1 -0
- package/dist/utils/convert-strings.js +30 -0
- package/dist/utils/convert-strings.js.map +1 -0
- package/dist/utils/sanitize.d.ts +2 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +9 -0
- package/dist/utils/sanitize.js.map +1 -0
- package/dist/zip-foundry.js +4 -4
- package/package.json +5 -6
- package/src/account.ts +292 -0
- package/src/api.ts +16 -7
- package/src/build-generic.ts +6 -0
- package/src/environments/hardhat/package-lock.json +3 -3
- package/src/environments/hardhat/upgradeable/package-lock.json +7 -7
- package/src/erc20.ts +47 -1
- package/src/generate/account.ts +20 -0
- package/src/generate/erc20.ts +1 -0
- package/src/generate/sources.ts +7 -0
- package/src/generate/stablecoin.ts +2 -0
- package/src/governor.ts +9 -8
- package/src/index.ts +1 -1
- package/src/kind.ts +2 -1
- package/src/print.ts +2 -1
- package/src/signer.ts +124 -0
- package/src/solidity-version.json +1 -1
- package/src/stablecoin.ts +2 -2
- package/src/test.ts +17 -5
- package/src/utils/convert-strings.ts +27 -0
- package/src/utils/sanitize.ts +6 -0
- package/src/zip-foundry.ts +4 -4
package/src/api.ts
CHANGED
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
defaults as stablecoinDefaults,
|
|
24
24
|
isAccessControlRequired as stablecoinIsAccessControlRequired,
|
|
25
25
|
} from './stablecoin';
|
|
26
|
+
import type { AccountOptions } from './account';
|
|
27
|
+
import { printAccount, defaults as accountDefaults } from './account';
|
|
26
28
|
import type { GovernorOptions } from './governor';
|
|
27
29
|
import {
|
|
28
30
|
printGovernor,
|
|
@@ -46,7 +48,9 @@ export interface WizardContractAPI<Options extends CommonOptions> {
|
|
|
46
48
|
* The default options that are used for `print`.
|
|
47
49
|
*/
|
|
48
50
|
defaults: Required<Options>;
|
|
51
|
+
}
|
|
49
52
|
|
|
53
|
+
export interface AccessControlAPI<Options extends CommonOptions> {
|
|
50
54
|
/**
|
|
51
55
|
* Whether any of the provided options require access control to be enabled. If this returns `true`, then calling `print` with the
|
|
52
56
|
* same options would cause the `access` option to default to `'ownable'` if it was `undefined` or `false`.
|
|
@@ -54,13 +58,14 @@ export interface WizardContractAPI<Options extends CommonOptions> {
|
|
|
54
58
|
isAccessControlRequired: (opts: Partial<Options>) => boolean;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
export type ERC20 = WizardContractAPI<ERC20Options>;
|
|
58
|
-
export type ERC721 = WizardContractAPI<ERC721Options>;
|
|
59
|
-
export type ERC1155 = WizardContractAPI<ERC1155Options>;
|
|
60
|
-
export type Stablecoin = WizardContractAPI<StablecoinOptions>;
|
|
61
|
-
export type RealWorldAsset = WizardContractAPI<StablecoinOptions>;
|
|
62
|
-
export type
|
|
63
|
-
export type
|
|
61
|
+
export type ERC20 = WizardContractAPI<ERC20Options> & AccessControlAPI<ERC20Options>;
|
|
62
|
+
export type ERC721 = WizardContractAPI<ERC721Options> & AccessControlAPI<ERC721Options>;
|
|
63
|
+
export type ERC1155 = WizardContractAPI<ERC1155Options> & AccessControlAPI<ERC1155Options>;
|
|
64
|
+
export type Stablecoin = WizardContractAPI<StablecoinOptions> & AccessControlAPI<StablecoinOptions>;
|
|
65
|
+
export type RealWorldAsset = WizardContractAPI<StablecoinOptions> & AccessControlAPI<StablecoinOptions>;
|
|
66
|
+
export type Account = WizardContractAPI<AccountOptions>;
|
|
67
|
+
export type Governor = WizardContractAPI<GovernorOptions> & AccessControlAPI<GovernorOptions>;
|
|
68
|
+
export type Custom = WizardContractAPI<CustomOptions> & AccessControlAPI<CustomOptions>;
|
|
64
69
|
|
|
65
70
|
export const erc20: ERC20 = {
|
|
66
71
|
print: printERC20,
|
|
@@ -82,6 +87,10 @@ export const stablecoin: Stablecoin = {
|
|
|
82
87
|
defaults: stablecoinDefaults,
|
|
83
88
|
isAccessControlRequired: stablecoinIsAccessControlRequired,
|
|
84
89
|
};
|
|
90
|
+
export const account: Account = {
|
|
91
|
+
print: printAccount,
|
|
92
|
+
defaults: accountDefaults,
|
|
93
|
+
};
|
|
85
94
|
export const realWorldAsset: RealWorldAsset = {
|
|
86
95
|
print: printStablecoin,
|
|
87
96
|
defaults: stablecoinDefaults,
|
package/src/build-generic.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { buildStablecoin } from './stablecoin';
|
|
|
11
11
|
import type { GovernorOptions } from './governor';
|
|
12
12
|
import { buildGovernor } from './governor';
|
|
13
13
|
import type { Contract } from './contract';
|
|
14
|
+
import { buildAccount } from './account';
|
|
15
|
+
import type { AccountOptions } from './account';
|
|
14
16
|
|
|
15
17
|
export interface KindedOptions {
|
|
16
18
|
ERC20: { kind: 'ERC20' } & ERC20Options;
|
|
@@ -18,6 +20,7 @@ export interface KindedOptions {
|
|
|
18
20
|
ERC1155: { kind: 'ERC1155' } & ERC1155Options;
|
|
19
21
|
Stablecoin: { kind: 'Stablecoin' } & StablecoinOptions;
|
|
20
22
|
RealWorldAsset: { kind: 'RealWorldAsset' } & StablecoinOptions;
|
|
23
|
+
Account: { kind: 'Account' } & AccountOptions;
|
|
21
24
|
Governor: { kind: 'Governor' } & GovernorOptions;
|
|
22
25
|
Custom: { kind: 'Custom' } & CustomOptions;
|
|
23
26
|
}
|
|
@@ -41,6 +44,9 @@ export function buildGeneric(opts: GenericOptions): Contract {
|
|
|
41
44
|
case 'RealWorldAsset':
|
|
42
45
|
return buildStablecoin(opts);
|
|
43
46
|
|
|
47
|
+
case 'Account':
|
|
48
|
+
return buildAccount(opts);
|
|
49
|
+
|
|
44
50
|
case 'Governor':
|
|
45
51
|
return buildGovernor(opts);
|
|
46
52
|
|
|
@@ -1510,9 +1510,9 @@
|
|
|
1510
1510
|
}
|
|
1511
1511
|
},
|
|
1512
1512
|
"node_modules/@openzeppelin/contracts": {
|
|
1513
|
-
"version": "5.
|
|
1514
|
-
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.
|
|
1515
|
-
"integrity": "sha512-
|
|
1513
|
+
"version": "5.3.0",
|
|
1514
|
+
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.3.0.tgz",
|
|
1515
|
+
"integrity": "sha512-zj/KGoW7zxWUE8qOI++rUM18v+VeLTTzKs/DJFkSzHpQFPD/jKKF0TrMxBfGLl3kpdELCNccvB3zmofSzm4nlA==",
|
|
1516
1516
|
"dev": true,
|
|
1517
1517
|
"license": "MIT"
|
|
1518
1518
|
},
|
|
@@ -2195,20 +2195,20 @@
|
|
|
2195
2195
|
}
|
|
2196
2196
|
},
|
|
2197
2197
|
"node_modules/@openzeppelin/contracts": {
|
|
2198
|
-
"version": "5.
|
|
2199
|
-
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.
|
|
2200
|
-
"integrity": "sha512-
|
|
2198
|
+
"version": "5.3.0",
|
|
2199
|
+
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.3.0.tgz",
|
|
2200
|
+
"integrity": "sha512-zj/KGoW7zxWUE8qOI++rUM18v+VeLTTzKs/DJFkSzHpQFPD/jKKF0TrMxBfGLl3kpdELCNccvB3zmofSzm4nlA==",
|
|
2201
2201
|
"dev": true,
|
|
2202
2202
|
"license": "MIT"
|
|
2203
2203
|
},
|
|
2204
2204
|
"node_modules/@openzeppelin/contracts-upgradeable": {
|
|
2205
|
-
"version": "5.
|
|
2206
|
-
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.
|
|
2207
|
-
"integrity": "sha512-
|
|
2205
|
+
"version": "5.3.0",
|
|
2206
|
+
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.3.0.tgz",
|
|
2207
|
+
"integrity": "sha512-yVzSSyTMWO6rapGI5tuqkcLpcGGXA0UA1vScyV5EhE5yw8By3Ewex9rDUw8lfVw0iTkvR/egjfcW5vpk03lqZg==",
|
|
2208
2208
|
"dev": true,
|
|
2209
2209
|
"license": "MIT",
|
|
2210
2210
|
"peerDependencies": {
|
|
2211
|
-
"@openzeppelin/contracts": "5.
|
|
2211
|
+
"@openzeppelin/contracts": "5.3.0"
|
|
2212
2212
|
}
|
|
2213
2213
|
},
|
|
2214
2214
|
"node_modules/@openzeppelin/defender-sdk-base-client": {
|
package/src/erc20.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type { ClockMode } from './set-clock-mode';
|
|
|
13
13
|
import { clockModeDefault, setClockMode } from './set-clock-mode';
|
|
14
14
|
import { supportsInterface } from './common-functions';
|
|
15
15
|
import { OptionsError } from './error';
|
|
16
|
+
import { toUint256, UINT256_MAX } from './utils/convert-strings';
|
|
16
17
|
|
|
17
18
|
export const crossChainBridgingOptions = [false, 'custom', 'superchain'] as const;
|
|
18
19
|
export type CrossChainBridging = (typeof crossChainBridgingOptions)[number];
|
|
@@ -25,6 +26,7 @@ export interface ERC20Options extends CommonOptions {
|
|
|
25
26
|
premint?: string;
|
|
26
27
|
premintChainId?: string;
|
|
27
28
|
mintable?: boolean;
|
|
29
|
+
callback?: boolean;
|
|
28
30
|
permit?: boolean;
|
|
29
31
|
/**
|
|
30
32
|
* Whether to keep track of historical balances for voting in on-chain governance, and optionally specify the clock mode.
|
|
@@ -43,6 +45,7 @@ export const defaults: Required<ERC20Options> = {
|
|
|
43
45
|
premint: '0',
|
|
44
46
|
premintChainId: '',
|
|
45
47
|
mintable: false,
|
|
48
|
+
callback: false,
|
|
46
49
|
permit: true,
|
|
47
50
|
votes: false,
|
|
48
51
|
flashmint: false,
|
|
@@ -61,6 +64,7 @@ export function withDefaults(opts: ERC20Options): Required<ERC20Options> {
|
|
|
61
64
|
premint: opts.premint || defaults.premint,
|
|
62
65
|
premintChainId: opts.premintChainId || defaults.premintChainId,
|
|
63
66
|
mintable: opts.mintable ?? defaults.mintable,
|
|
67
|
+
callback: opts.callback ?? defaults.callback,
|
|
64
68
|
permit: opts.permit ?? defaults.permit,
|
|
65
69
|
votes: opts.votes ?? defaults.votes,
|
|
66
70
|
flashmint: opts.flashmint ?? defaults.flashmint,
|
|
@@ -105,6 +109,10 @@ export function buildERC20(opts: ERC20Options): ContractBuilder {
|
|
|
105
109
|
addMintable(c, access);
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
if (allOpts.callback) {
|
|
113
|
+
addCallback(c);
|
|
114
|
+
}
|
|
115
|
+
|
|
108
116
|
// Note: Votes requires Permit
|
|
109
117
|
if (allOpts.permit || allOpts.votes) {
|
|
110
118
|
addPermit(c, allOpts.name);
|
|
@@ -163,6 +171,14 @@ export function isValidChainId(str: string): boolean {
|
|
|
163
171
|
return chainIdPattern.test(str);
|
|
164
172
|
}
|
|
165
173
|
|
|
174
|
+
function scaleByPowerOfTen(base: bigint, exponent: number): bigint {
|
|
175
|
+
if (exponent < 0) {
|
|
176
|
+
return base / BigInt(10) ** BigInt(-exponent);
|
|
177
|
+
} else {
|
|
178
|
+
return base * BigInt(10) ** BigInt(exponent);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
166
182
|
function addPremint(
|
|
167
183
|
c: ContractBuilder,
|
|
168
184
|
amount: string,
|
|
@@ -181,6 +197,9 @@ function addPremint(
|
|
|
181
197
|
const units = integer + decimals + zeroes;
|
|
182
198
|
const exp = decimalPlace <= 0 ? 'decimals()' : `(decimals() - ${decimalPlace})`;
|
|
183
199
|
|
|
200
|
+
const validatedBaseUnits = toUint256(units, 'premint');
|
|
201
|
+
checkPotentialPremintOverflow(validatedBaseUnits, decimalPlace);
|
|
202
|
+
|
|
184
203
|
c.addConstructorArgument({ type: 'address', name: 'recipient' });
|
|
185
204
|
|
|
186
205
|
const mintLine = `_mint(recipient, ${units} * 10 ** ${exp});`;
|
|
@@ -212,11 +231,38 @@ function addPremint(
|
|
|
212
231
|
}
|
|
213
232
|
}
|
|
214
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Check for potential premint overflow assuming the user's contract has decimals() = 18
|
|
236
|
+
*
|
|
237
|
+
* @param baseUnits The base units of the token, before applying power of 10
|
|
238
|
+
* @param decimalPlace If positive, the number of assumed decimal places in the least significant digits of `validatedBaseUnits`. Ignored if <= 0.
|
|
239
|
+
* @throws OptionsError if the calculated value would overflow uint256
|
|
240
|
+
*/
|
|
241
|
+
function checkPotentialPremintOverflow(baseUnits: bigint, decimalPlace: number) {
|
|
242
|
+
const assumedExp = decimalPlace <= 0 ? 18 : 18 - decimalPlace;
|
|
243
|
+
const calculatedValue = scaleByPowerOfTen(baseUnits, assumedExp);
|
|
244
|
+
|
|
245
|
+
if (calculatedValue > UINT256_MAX) {
|
|
246
|
+
throw new OptionsError({
|
|
247
|
+
premint: 'Amount would overflow uint256 after applying decimals',
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
215
252
|
function addMintable(c: ContractBuilder, access: Access) {
|
|
216
253
|
requireAccessControl(c, functions.mint, access, 'MINTER', 'minter');
|
|
217
254
|
c.addFunctionCode('_mint(to, amount);', functions.mint);
|
|
218
255
|
}
|
|
219
256
|
|
|
257
|
+
function addCallback(c: ContractBuilder) {
|
|
258
|
+
const ERC1363 = {
|
|
259
|
+
name: 'ERC1363',
|
|
260
|
+
path: '@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol',
|
|
261
|
+
};
|
|
262
|
+
c.addParent(ERC1363);
|
|
263
|
+
c.addOverride(ERC1363, supportsInterface);
|
|
264
|
+
}
|
|
265
|
+
|
|
220
266
|
function addPermit(c: ContractBuilder, name: string) {
|
|
221
267
|
const ERC20Permit = {
|
|
222
268
|
name: 'ERC20Permit',
|
|
@@ -267,7 +313,7 @@ function addCrossChainBridging(
|
|
|
267
313
|
) {
|
|
268
314
|
const ERC20Bridgeable = {
|
|
269
315
|
name: 'ERC20Bridgeable',
|
|
270
|
-
path: `@openzeppelin/community-contracts/
|
|
316
|
+
path: `@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Bridgeable.sol`,
|
|
271
317
|
};
|
|
272
318
|
|
|
273
319
|
c.addParent(ERC20Bridgeable);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AccountOptions } from '../account';
|
|
2
|
+
import { infoOptions } from '../set-info';
|
|
3
|
+
import { generateAlternatives } from './alternatives';
|
|
4
|
+
|
|
5
|
+
const account = {
|
|
6
|
+
name: ['MyAccount'],
|
|
7
|
+
signatureValidation: [false, 'ERC1271', 'ERC7739'] as const,
|
|
8
|
+
ERC721Holder: [false, true] as const,
|
|
9
|
+
ERC1155Holder: [false, true] as const,
|
|
10
|
+
signer: ['ERC7702', 'ECDSA', 'P256', 'RSA', 'Multisig', 'MultisigWeighted'] as const,
|
|
11
|
+
batchedExecution: [false, true] as const,
|
|
12
|
+
ERC7579Modules: [false, 'AccountERC7579', 'AccountERC7579Hooked'] as const,
|
|
13
|
+
access: [false] as const,
|
|
14
|
+
upgradeable: [false] as const,
|
|
15
|
+
info: infoOptions,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function* generateAccountOptions(): Generator<Required<AccountOptions>> {
|
|
19
|
+
yield* generateAlternatives(account);
|
|
20
|
+
}
|
package/src/generate/erc20.ts
CHANGED
package/src/generate/sources.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { generateERC20Options } from './erc20';
|
|
|
6
6
|
import { generateERC721Options } from './erc721';
|
|
7
7
|
import { generateERC1155Options } from './erc1155';
|
|
8
8
|
import { generateStablecoinOptions } from './stablecoin';
|
|
9
|
+
import { generateAccountOptions } from './account';
|
|
9
10
|
import { generateGovernorOptions } from './governor';
|
|
10
11
|
import { generateCustomOptions } from './custom';
|
|
11
12
|
import type { GenericOptions, KindedOptions } from '../build-generic';
|
|
@@ -50,6 +51,12 @@ export function* generateOptions(kind?: Kind): Generator<GenericOptions> {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
if (!kind || kind === 'Account') {
|
|
55
|
+
for (const kindOpts of generateAccountOptions()) {
|
|
56
|
+
yield { kind: 'Account', ...kindOpts };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
53
60
|
if (!kind || kind === 'Governor') {
|
|
54
61
|
for (const kindOpts of generateGovernorOptions()) {
|
|
55
62
|
yield { kind: 'Governor', ...kindOpts };
|
|
@@ -12,6 +12,7 @@ const erc20Basic = {
|
|
|
12
12
|
burnable: [false] as const,
|
|
13
13
|
pausable: [false] as const,
|
|
14
14
|
mintable: [false] as const,
|
|
15
|
+
callback: [false] as const,
|
|
15
16
|
permit: [false] as const,
|
|
16
17
|
votes: [false] as const,
|
|
17
18
|
flashmint: [false] as const,
|
|
@@ -28,6 +29,7 @@ const erc20Full = {
|
|
|
28
29
|
burnable: [true] as const,
|
|
29
30
|
pausable: [true] as const,
|
|
30
31
|
mintable: [true] as const,
|
|
32
|
+
callback: [true] as const,
|
|
31
33
|
permit: [true] as const,
|
|
32
34
|
votes: ['timestamp'] as const,
|
|
33
35
|
flashmint: [true] as const,
|
package/src/governor.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { supportsInterface } from './common-functions';
|
|
2
2
|
import type { CommonOptions } from './common-options';
|
|
3
3
|
import { withCommonDefaults, defaults as commonDefaults } from './common-options';
|
|
4
|
-
import type { Contract } from './contract';
|
|
4
|
+
import type { Contract, ImportContract } from './contract';
|
|
5
5
|
import { ContractBuilder } from './contract';
|
|
6
6
|
import { OptionsError } from './error';
|
|
7
7
|
import { setAccessControl } from './set-access-control';
|
|
@@ -105,15 +105,13 @@ export function buildGovernor(opts: GovernorOptions): Contract {
|
|
|
105
105
|
return c;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
const Governor: ImportContract = {
|
|
109
|
+
name: 'Governor',
|
|
110
|
+
path: '@openzeppelin/contracts/governance/Governor.sol',
|
|
111
|
+
};
|
|
112
|
+
|
|
108
113
|
function addBase(c: ContractBuilder, { name }: GovernorOptions) {
|
|
109
|
-
const Governor = {
|
|
110
|
-
name: 'Governor',
|
|
111
|
-
path: '@openzeppelin/contracts/governance/Governor.sol',
|
|
112
|
-
};
|
|
113
114
|
c.addParent(Governor, [name]);
|
|
114
|
-
c.addOverride(Governor, functions.votingDelay);
|
|
115
|
-
c.addOverride(Governor, functions.votingPeriod);
|
|
116
|
-
c.addOverride(Governor, functions.quorum);
|
|
117
115
|
c.addOverride(Governor, functions.state);
|
|
118
116
|
c.addOverride(Governor, functions.propose);
|
|
119
117
|
c.addOverride(Governor, functions.proposalNeedsQueuing);
|
|
@@ -221,6 +219,7 @@ function setVotingParameters(c: ContractBuilder, opts: Required<GovernorOptions>
|
|
|
221
219
|
} else {
|
|
222
220
|
c.setFunctionBody([`return ${delayBlocks.value}; // ${delayBlocks.note}`], functions.votingDelay);
|
|
223
221
|
}
|
|
222
|
+
c.addOverride(Governor, functions.votingDelay);
|
|
224
223
|
|
|
225
224
|
const periodBlocks = getVotingPeriod(opts);
|
|
226
225
|
if ('lit' in periodBlocks) {
|
|
@@ -228,6 +227,7 @@ function setVotingParameters(c: ContractBuilder, opts: Required<GovernorOptions>
|
|
|
228
227
|
} else {
|
|
229
228
|
c.setFunctionBody([`return ${periodBlocks.value}; // ${periodBlocks.note}`], functions.votingPeriod);
|
|
230
229
|
}
|
|
230
|
+
c.addOverride(Governor, functions.votingPeriod);
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
function setProposalThreshold(c: ContractBuilder, opts: Required<GovernorOptions>) {
|
|
@@ -308,6 +308,7 @@ function addQuorum(c: ContractBuilder, opts: Required<GovernorOptions>) {
|
|
|
308
308
|
: `return ${opts.quorumAbsolute}e${opts.decimals};`;
|
|
309
309
|
|
|
310
310
|
c.setFunctionBody([returnStatement], functions.quorum, 'pure');
|
|
311
|
+
c.addOverride(Governor, functions.quorum);
|
|
311
312
|
}
|
|
312
313
|
}
|
|
313
314
|
|
package/src/index.ts
CHANGED
|
@@ -19,6 +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, stablecoin, realWorldAsset, governor, custom } from './api';
|
|
22
|
+
export { erc20, erc721, erc1155, stablecoin, realWorldAsset, account, governor, custom } from './api';
|
|
23
23
|
|
|
24
24
|
export { compatibleContractsSemver } from './utils/version';
|
package/src/kind.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type Kind = GenericOptions['kind'];
|
|
|
4
4
|
|
|
5
5
|
export function sanitizeKind(kind: unknown): Kind {
|
|
6
6
|
if (typeof kind === 'string') {
|
|
7
|
-
const sanitized = kind.replace(/^(ERC|.)/i, c => c.toUpperCase());
|
|
7
|
+
const sanitized = kind.replace(/^(ERC|.)/i, c => c.toUpperCase()).replace(/^(RealWorldAsset)$/i, 'RealWorldAsset');
|
|
8
8
|
if (isKind(sanitized)) {
|
|
9
9
|
return sanitized;
|
|
10
10
|
}
|
|
@@ -19,6 +19,7 @@ function isKind<T>(value: Kind | T): value is Kind {
|
|
|
19
19
|
case 'ERC721':
|
|
20
20
|
case 'Stablecoin':
|
|
21
21
|
case 'RealWorldAsset':
|
|
22
|
+
case 'Account':
|
|
22
23
|
case 'Governor':
|
|
23
24
|
case 'Custom':
|
|
24
25
|
return true;
|
package/src/print.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { mapValues } from './utils/map-values';
|
|
|
16
16
|
import SOLIDITY_VERSION from './solidity-version.json';
|
|
17
17
|
import { inferTranspiled } from './infer-transpiled';
|
|
18
18
|
import { compatibleContractsSemver } from './utils/version';
|
|
19
|
+
import { stringifyUnicodeSafe } from './utils/sanitize';
|
|
19
20
|
|
|
20
21
|
export function printContract(contract: Contract, opts?: Options): string {
|
|
21
22
|
const helpers = withHelpers(contract, opts);
|
|
@@ -148,7 +149,7 @@ export function printValue(value: Value): string {
|
|
|
148
149
|
throw new Error(`Number not representable (${value})`);
|
|
149
150
|
}
|
|
150
151
|
} else {
|
|
151
|
-
return
|
|
152
|
+
return stringifyUnicodeSafe(value);
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
|
package/src/signer.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { ContractBuilder } from './contract';
|
|
2
|
+
import { defineFunctions } from './utils/define-functions';
|
|
3
|
+
|
|
4
|
+
export const SignerOptions = [false, 'ERC7702', 'ECDSA', 'P256', 'RSA', 'Multisig', 'MultisigWeighted'] as const;
|
|
5
|
+
export type SignerOptions = (typeof SignerOptions)[number];
|
|
6
|
+
|
|
7
|
+
export function addSigner(c: ContractBuilder, signer: SignerOptions): void {
|
|
8
|
+
if (!signer) return;
|
|
9
|
+
const parent = signers[signer];
|
|
10
|
+
const name = parent.name;
|
|
11
|
+
c.addParent(parent);
|
|
12
|
+
c.addOverride(
|
|
13
|
+
{ name: name === signers.MultisigWeighted.name ? signers.Multisig.name : name },
|
|
14
|
+
signerFunctions._rawSignatureValidation,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// ERC-7702 doesn't require initialization
|
|
18
|
+
if (signer === 'ERC7702') return;
|
|
19
|
+
|
|
20
|
+
c.addParent({
|
|
21
|
+
name: 'Initializable',
|
|
22
|
+
path: '@openzeppelin/contracts/proxy/utils/Initializable.sol',
|
|
23
|
+
});
|
|
24
|
+
const fn = signerFunctions[`initialize${signer}`];
|
|
25
|
+
c.addModifier('initializer', fn);
|
|
26
|
+
|
|
27
|
+
const args = fn.args;
|
|
28
|
+
|
|
29
|
+
switch (signer) {
|
|
30
|
+
case 'Multisig':
|
|
31
|
+
c.addFunctionCode(`_addSigners(${args[0]!.name});`, fn);
|
|
32
|
+
c.addFunctionCode(`_setThreshold(${args[1]!.name});`, fn);
|
|
33
|
+
break;
|
|
34
|
+
case 'MultisigWeighted':
|
|
35
|
+
c.addFunctionCode(`_addSigners(${args[0]!.name});`, fn);
|
|
36
|
+
c.addFunctionCode(`_setSignerWeights(${args[0]!.name}, ${args[1]!.name});`, fn);
|
|
37
|
+
c.addFunctionCode(`_setThreshold(${args[2]!.name});`, fn);
|
|
38
|
+
break;
|
|
39
|
+
case 'ECDSA':
|
|
40
|
+
case 'P256':
|
|
41
|
+
case 'RSA':
|
|
42
|
+
c.addFunctionCode(
|
|
43
|
+
`_setSigner(${fn.args
|
|
44
|
+
.map(({ name }) => name)
|
|
45
|
+
.join(', ')
|
|
46
|
+
.trimEnd()});`,
|
|
47
|
+
fn,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const signers = {
|
|
53
|
+
ERC7702: {
|
|
54
|
+
name: 'SignerERC7702',
|
|
55
|
+
path: '@openzeppelin/community-contracts/utils/cryptography/SignerERC7702.sol',
|
|
56
|
+
},
|
|
57
|
+
ECDSA: {
|
|
58
|
+
name: 'SignerECDSA',
|
|
59
|
+
path: '@openzeppelin/community-contracts/utils/cryptography/SignerECDSA.sol',
|
|
60
|
+
},
|
|
61
|
+
P256: {
|
|
62
|
+
name: 'SignerP256',
|
|
63
|
+
path: '@openzeppelin/community-contracts/utils/cryptography/SignerP256.sol',
|
|
64
|
+
},
|
|
65
|
+
RSA: {
|
|
66
|
+
name: 'SignerRSA',
|
|
67
|
+
path: '@openzeppelin/community-contracts/utils/cryptography/SignerRSA.sol',
|
|
68
|
+
},
|
|
69
|
+
Multisig: {
|
|
70
|
+
name: 'MultiSignerERC7913',
|
|
71
|
+
path: '@openzeppelin/community-contracts/utils/cryptography/MultiSignerERC7913.sol',
|
|
72
|
+
},
|
|
73
|
+
MultisigWeighted: {
|
|
74
|
+
name: 'MultiSignerERC7913Weighted',
|
|
75
|
+
path: '@openzeppelin/community-contracts/utils/cryptography/MultiSignerERC7913Weighted.sol',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const signerFunctions = {
|
|
80
|
+
...defineFunctions({
|
|
81
|
+
initializeECDSA: {
|
|
82
|
+
kind: 'public' as const,
|
|
83
|
+
args: [{ name: 'signer', type: 'address' }],
|
|
84
|
+
},
|
|
85
|
+
initializeP256: {
|
|
86
|
+
kind: 'public' as const,
|
|
87
|
+
args: [
|
|
88
|
+
{ name: 'qx', type: 'bytes32' },
|
|
89
|
+
{ name: 'qy', type: 'bytes32' },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
initializeRSA: {
|
|
93
|
+
kind: 'public' as const,
|
|
94
|
+
args: [
|
|
95
|
+
{ name: 'e', type: 'bytes memory' },
|
|
96
|
+
{ name: 'n', type: 'bytes memory' },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
initializeMultisig: {
|
|
100
|
+
kind: 'public' as const,
|
|
101
|
+
args: [
|
|
102
|
+
{ name: 'signers', type: 'bytes[] memory' },
|
|
103
|
+
{ name: 'threshold', type: 'uint256' },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
initializeMultisigWeighted: {
|
|
107
|
+
kind: 'public' as const,
|
|
108
|
+
args: [
|
|
109
|
+
{ name: 'signers', type: 'bytes[] memory' },
|
|
110
|
+
{ name: 'weights', type: 'uint256[] memory' },
|
|
111
|
+
{ name: 'threshold', type: 'uint256' },
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
_rawSignatureValidation: {
|
|
115
|
+
kind: 'internal' as const,
|
|
116
|
+
args: [
|
|
117
|
+
{ name: 'hash', type: 'bytes32' },
|
|
118
|
+
{ name: 'signature', type: 'bytes calldata' },
|
|
119
|
+
],
|
|
120
|
+
returns: ['bool'],
|
|
121
|
+
mutability: 'view' as const,
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"0.8.
|
|
1
|
+
"0.8.27"
|
package/src/stablecoin.ts
CHANGED
|
@@ -65,7 +65,7 @@ function addLimitations(c: ContractBuilder, access: Access, mode: boolean | 'all
|
|
|
65
65
|
const type = mode === 'allowlist';
|
|
66
66
|
const ERC20Limitation = {
|
|
67
67
|
name: type ? 'ERC20Allowlist' : 'ERC20Blocklist',
|
|
68
|
-
path: `@openzeppelin/community-contracts/
|
|
68
|
+
path: `@openzeppelin/community-contracts/token/ERC20/extensions/${type ? 'ERC20Allowlist' : 'ERC20Blocklist'}.sol`,
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
c.addParent(ERC20Limitation);
|
|
@@ -86,7 +86,7 @@ function addLimitations(c: ContractBuilder, access: Access, mode: boolean | 'all
|
|
|
86
86
|
function addCustodian(c: ContractBuilder, access: Access) {
|
|
87
87
|
const ERC20Custodian = {
|
|
88
88
|
name: 'ERC20Custodian',
|
|
89
|
-
path: '@openzeppelin/community-contracts/
|
|
89
|
+
path: '@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Custodian.sol',
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
c.addParent(ERC20Custodian);
|
package/src/test.ts
CHANGED
|
@@ -30,6 +30,10 @@ test.serial('stablecoin result compiles', async t => {
|
|
|
30
30
|
await testCompile(t, 'Stablecoin');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
test.serial('account result compiles', async t => {
|
|
34
|
+
await testCompile(t, 'Account');
|
|
35
|
+
});
|
|
36
|
+
|
|
33
37
|
test.serial('governor result compiles', async t => {
|
|
34
38
|
await testCompile(t, 'Governor');
|
|
35
39
|
});
|
|
@@ -66,6 +70,8 @@ function isAccessControlRequired(opts: GenericOptions) {
|
|
|
66
70
|
return stablecoin.isAccessControlRequired(opts);
|
|
67
71
|
case 'RealWorldAsset':
|
|
68
72
|
return stablecoin.isAccessControlRequired(opts);
|
|
73
|
+
case 'Account':
|
|
74
|
+
throw new Error(`Not applicable for ${opts.kind}`);
|
|
69
75
|
case 'Governor':
|
|
70
76
|
return governor.isAccessControlRequired(opts);
|
|
71
77
|
case 'Custom':
|
|
@@ -79,11 +85,17 @@ test('is access control required', async t => {
|
|
|
79
85
|
for (const contract of generateSources('all')) {
|
|
80
86
|
const regexOwnable = /import.*Ownable(Upgradeable)?.sol.*/gm;
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
switch (contract.options.kind) {
|
|
89
|
+
case 'Account':
|
|
90
|
+
break;
|
|
91
|
+
default: {
|
|
92
|
+
if (!contract.options.access) {
|
|
93
|
+
if (isAccessControlRequired(contract.options)) {
|
|
94
|
+
t.regex(contract.source, regexOwnable, JSON.stringify(contract.options));
|
|
95
|
+
} else {
|
|
96
|
+
t.notRegex(contract.source, regexOwnable, JSON.stringify(contract.options));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
87
99
|
}
|
|
88
100
|
}
|
|
89
101
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { OptionsError } from '../error';
|
|
2
|
+
|
|
3
|
+
export const UINT256_MAX = BigInt(2) ** BigInt(256) - BigInt(1);
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks that a string is a valid `uint256` value and converts it to bigint.
|
|
7
|
+
*
|
|
8
|
+
* @param value The value to check.
|
|
9
|
+
* @param field The field name to use in the error if the value is invalid.
|
|
10
|
+
* @throws OptionsError if the value is not a valid number or is greater than the maximum value for `uint256`.
|
|
11
|
+
* @returns The value as a bigint.
|
|
12
|
+
*/
|
|
13
|
+
export function toUint256(value: string, field: string): bigint {
|
|
14
|
+
const isValidNumber = /^\d+$/.test(value);
|
|
15
|
+
if (!isValidNumber) {
|
|
16
|
+
throw new OptionsError({
|
|
17
|
+
[field]: 'Not a valid number',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const numValue = BigInt(value);
|
|
21
|
+
if (numValue > UINT256_MAX) {
|
|
22
|
+
throw new OptionsError({
|
|
23
|
+
[field]: 'Value is greater than uint256 max value',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return numValue;
|
|
27
|
+
}
|
package/src/zip-foundry.ts
CHANGED
|
@@ -202,18 +202,18 @@ then
|
|
|
202
202
|
mv README.md README-oz.md
|
|
203
203
|
|
|
204
204
|
# Initialize sample Foundry project
|
|
205
|
-
forge init --force --
|
|
205
|
+
forge init --force --quiet
|
|
206
206
|
|
|
207
207
|
${
|
|
208
208
|
c.upgradeable
|
|
209
209
|
? `\
|
|
210
210
|
# Install OpenZeppelin Contracts and Upgrades
|
|
211
|
-
forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v${contracts.version} --
|
|
212
|
-
forge install OpenZeppelin/openzeppelin-foundry-upgrades --
|
|
211
|
+
forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v${contracts.version} --quiet
|
|
212
|
+
forge install OpenZeppelin/openzeppelin-foundry-upgrades --quiet\
|
|
213
213
|
`
|
|
214
214
|
: `\
|
|
215
215
|
# Install OpenZeppelin Contracts
|
|
216
|
-
forge install OpenZeppelin/openzeppelin-contracts@v${contracts.version} --
|
|
216
|
+
forge install OpenZeppelin/openzeppelin-contracts@v${contracts.version} --quiet\
|
|
217
217
|
`
|
|
218
218
|
}
|
|
219
219
|
|