@openzeppelin/wizard 0.8.0 → 0.9.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.
- package/dist/account.js +8 -10
- package/dist/account.js.map +1 -1
- package/dist/common-options.js +3 -4
- package/dist/common-options.js.map +1 -1
- package/dist/contract.d.ts +25 -6
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +48 -7
- package/dist/contract.js.map +1 -1
- package/dist/custom.js +1 -2
- package/dist/custom.js.map +1 -1
- package/dist/erc1155.d.ts.map +1 -1
- package/dist/erc1155.js +6 -9
- package/dist/erc1155.js.map +1 -1
- package/dist/erc20.d.ts +1 -0
- package/dist/erc20.d.ts.map +1 -1
- package/dist/erc20.js +36 -27
- package/dist/erc20.js.map +1 -1
- package/dist/erc721.d.ts +1 -0
- package/dist/erc721.d.ts.map +1 -1
- package/dist/erc721.js +25 -17
- package/dist/erc721.js.map +1 -1
- package/dist/generate/alternatives.js +1 -1
- package/dist/generate/alternatives.js.map +1 -1
- 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/erc721.d.ts.map +1 -1
- package/dist/generate/erc721.js +1 -0
- package/dist/generate/erc721.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.js +9 -10
- package/dist/governor.js.map +1 -1
- package/dist/infer-transpiled.js +1 -2
- package/dist/infer-transpiled.js.map +1 -1
- package/dist/options.js +1 -2
- package/dist/options.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +24 -4
- package/dist/print.js.map +1 -1
- package/dist/scripts/prepare.js +1 -2
- package/dist/scripts/prepare.js.map +1 -1
- package/dist/set-access-control.d.ts.map +1 -1
- package/dist/set-access-control.js +1 -1
- package/dist/set-access-control.js.map +1 -1
- package/dist/set-namespaced-storage.d.ts +7 -0
- package/dist/set-namespaced-storage.d.ts.map +1 -0
- package/dist/set-namespaced-storage.js +61 -0
- package/dist/set-namespaced-storage.js.map +1 -0
- package/dist/stablecoin.js +5 -6
- package/dist/stablecoin.js.map +1 -1
- package/dist/utils/namespaced-slot.d.ts +5 -0
- package/dist/utils/namespaced-slot.d.ts.map +1 -0
- package/dist/utils/namespaced-slot.js +18 -0
- package/dist/utils/namespaced-slot.js.map +1 -0
- package/dist/utils/transitive-closure.js +1 -2
- package/dist/utils/transitive-closure.js.map +1 -1
- package/dist/zip-foundry.js +4 -4
- package/dist/zip-foundry.js.map +1 -1
- package/package.json +5 -2
- package/src/contract.ts +64 -9
- package/src/erc1155.ts +1 -3
- package/src/erc20.ts +43 -14
- package/src/erc721.ts +25 -9
- package/src/generate/erc20.ts +1 -0
- package/src/generate/erc721.ts +1 -0
- package/src/generate/stablecoin.ts +2 -0
- package/src/print.ts +34 -3
- package/src/set-access-control.ts +3 -1
- package/src/set-namespaced-storage.ts +69 -0
- package/src/stablecoin.ts +3 -1
- package/src/utils/namespaced-slot.ts +18 -0
package/src/erc721.ts
CHANGED
|
@@ -8,10 +8,12 @@ import { defineFunctions } from './utils/define-functions';
|
|
|
8
8
|
import type { CommonOptions } from './common-options';
|
|
9
9
|
import { withCommonDefaults, defaults as commonDefaults } from './common-options';
|
|
10
10
|
import { setUpgradeable } from './set-upgradeable';
|
|
11
|
+
import type { Upgradeable } from './set-upgradeable';
|
|
11
12
|
import { setInfo } from './set-info';
|
|
12
13
|
import { printContract } from './print';
|
|
13
14
|
import type { ClockMode } from './set-clock-mode';
|
|
14
15
|
import { clockModeDefault, setClockMode } from './set-clock-mode';
|
|
16
|
+
import { setNamespacedStorage, toStorageStructInstantiation } from './set-namespaced-storage';
|
|
15
17
|
|
|
16
18
|
export interface ERC721Options extends CommonOptions {
|
|
17
19
|
name: string;
|
|
@@ -28,9 +30,11 @@ export interface ERC721Options extends CommonOptions {
|
|
|
28
30
|
* Setting `true` is equivalent to 'blocknumber'. Setting a clock mode implies voting is enabled.
|
|
29
31
|
*/
|
|
30
32
|
votes?: boolean | ClockMode;
|
|
33
|
+
namespacePrefix?: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export const defaults: Required<ERC721Options> = {
|
|
37
|
+
...commonDefaults,
|
|
34
38
|
name: 'MyToken',
|
|
35
39
|
symbol: 'MTK',
|
|
36
40
|
baseUri: '',
|
|
@@ -41,9 +45,7 @@ export const defaults: Required<ERC721Options> = {
|
|
|
41
45
|
mintable: false,
|
|
42
46
|
incremental: false,
|
|
43
47
|
votes: false,
|
|
44
|
-
|
|
45
|
-
upgradeable: commonDefaults.upgradeable,
|
|
46
|
-
info: commonDefaults.info,
|
|
48
|
+
namespacePrefix: 'myProject',
|
|
47
49
|
} as const;
|
|
48
50
|
|
|
49
51
|
function withDefaults(opts: ERC721Options): Required<ERC721Options> {
|
|
@@ -58,6 +60,7 @@ function withDefaults(opts: ERC721Options): Required<ERC721Options> {
|
|
|
58
60
|
mintable: opts.mintable ?? defaults.mintable,
|
|
59
61
|
incremental: opts.incremental ?? defaults.incremental,
|
|
60
62
|
votes: opts.votes ?? defaults.votes,
|
|
63
|
+
namespacePrefix: opts.namespacePrefix ?? defaults.namespacePrefix,
|
|
61
64
|
};
|
|
62
65
|
}
|
|
63
66
|
|
|
@@ -99,7 +102,7 @@ export function buildERC721(opts: ERC721Options): Contract {
|
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
if (allOpts.mintable) {
|
|
102
|
-
addMintable(c, access, allOpts.incremental, allOpts.uriStorage);
|
|
105
|
+
addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable, allOpts.namespacePrefix);
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
if (allOpts.votes) {
|
|
@@ -174,14 +177,27 @@ function addBurnable(c: ContractBuilder) {
|
|
|
174
177
|
});
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
function addMintable(
|
|
180
|
+
function addMintable(
|
|
181
|
+
c: ContractBuilder,
|
|
182
|
+
access: Access,
|
|
183
|
+
incremental = false,
|
|
184
|
+
uriStorage = false,
|
|
185
|
+
upgradeable: Upgradeable,
|
|
186
|
+
namespacePrefix: string,
|
|
187
|
+
) {
|
|
178
188
|
const fn = getMintFunction(incremental, uriStorage);
|
|
179
189
|
requireAccessControl(c, fn, access, 'MINTER', 'minter');
|
|
180
|
-
|
|
181
190
|
if (incremental) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
if (!upgradeable) {
|
|
192
|
+
c.addStateVariable('uint256 private _nextTokenId;', upgradeable);
|
|
193
|
+
c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn);
|
|
194
|
+
c.addFunctionCode('_safeMint(to, tokenId);', fn);
|
|
195
|
+
} else {
|
|
196
|
+
setNamespacedStorage(c, ['uint256 _nextTokenId;'], namespacePrefix);
|
|
197
|
+
c.addFunctionCode(toStorageStructInstantiation(c.name), fn);
|
|
198
|
+
c.addFunctionCode('uint256 tokenId = $._nextTokenId++;', fn);
|
|
199
|
+
c.addFunctionCode('_safeMint(to, tokenId);', fn);
|
|
200
|
+
}
|
|
185
201
|
} else {
|
|
186
202
|
c.addFunctionCode('_safeMint(to, tokenId);', fn);
|
|
187
203
|
}
|
package/src/generate/erc20.ts
CHANGED
package/src/generate/erc721.ts
CHANGED
|
@@ -21,6 +21,7 @@ const erc20Basic = {
|
|
|
21
21
|
crossChainBridging: [false] as const,
|
|
22
22
|
access: [false] as const,
|
|
23
23
|
info: [{}] as const,
|
|
24
|
+
namespacePrefix: ['myProject'],
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
const erc20Full = {
|
|
@@ -38,6 +39,7 @@ const erc20Full = {
|
|
|
38
39
|
crossChainBridging: crossChainBridgingOptions,
|
|
39
40
|
access: accessOptions,
|
|
40
41
|
info: infoOptions,
|
|
42
|
+
namespacePrefix: ['myProject'],
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
const stablecoinExtensions = {
|
package/src/print.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type {
|
|
|
6
6
|
Value,
|
|
7
7
|
NatspecTag,
|
|
8
8
|
ImportContract,
|
|
9
|
+
ContractStruct,
|
|
10
|
+
VariableOrErrorDefinition,
|
|
9
11
|
} from './contract';
|
|
10
12
|
import type { Options, Helpers } from './options';
|
|
11
13
|
import { withHelpers } from './options';
|
|
@@ -23,10 +25,9 @@ import { getCommunityContractsGitCommit } from './utils/community-contracts-git-
|
|
|
23
25
|
export function printContract(contract: Contract, opts?: Options): string {
|
|
24
26
|
const helpers = withHelpers(contract, opts);
|
|
25
27
|
|
|
28
|
+
const structs = contract.structs.map(_struct => printStruct(_struct));
|
|
26
29
|
const fns = mapValues(sortedFunctions(contract), fns => fns.map(fn => printFunction(fn, helpers)));
|
|
27
|
-
|
|
28
30
|
const hasOverrides = fns.override.some(l => l.length > 0);
|
|
29
|
-
|
|
30
31
|
return formatLines(
|
|
31
32
|
...spaceBetween(
|
|
32
33
|
[
|
|
@@ -42,7 +43,9 @@ export function printContract(contract: Contract, opts?: Options): string {
|
|
|
42
43
|
[`contract ${contract.name}`, ...printInheritance(contract, helpers), '{'].join(' '),
|
|
43
44
|
|
|
44
45
|
spaceBetween(
|
|
45
|
-
|
|
46
|
+
...structs,
|
|
47
|
+
printVariableOrErrorDefinitionsWithComments(contract.variableOrErrorDefinitions),
|
|
48
|
+
printVariableOrErrorDefinitionsWithoutComments(contract.variableOrErrorDefinitions),
|
|
46
49
|
printConstructor(contract, helpers),
|
|
47
50
|
...fns.code,
|
|
48
51
|
...fns.modifiers,
|
|
@@ -56,6 +59,20 @@ export function printContract(contract: Contract, opts?: Options): string {
|
|
|
56
59
|
);
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
function printVariableOrErrorDefinitionsWithComments(variableOrErrorDefinitions: VariableOrErrorDefinition[]): Lines[] {
|
|
63
|
+
const withComments = variableOrErrorDefinitions.filter(v => v.comments?.length);
|
|
64
|
+
// Spaces between each item that has comments
|
|
65
|
+
return spaceBetween(...withComments.map(v => [...v.comments!, v.code]));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function printVariableOrErrorDefinitionsWithoutComments(
|
|
69
|
+
variableOrErrorDefinitions: VariableOrErrorDefinition[],
|
|
70
|
+
): Lines[] {
|
|
71
|
+
const withoutComments = variableOrErrorDefinitions.filter(v => !v.comments?.length);
|
|
72
|
+
// No spaces between items that don't have comments
|
|
73
|
+
return withoutComments.map(v => v.code);
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
function printCompatibleLibraryVersions(contract: Contract): string {
|
|
60
77
|
let result = `// Compatible with OpenZeppelin Contracts ${compatibleContractsSemver}`;
|
|
61
78
|
if (importsCommunityContracts(contract)) {
|
|
@@ -258,6 +275,20 @@ function printFunction2(
|
|
|
258
275
|
return fn;
|
|
259
276
|
}
|
|
260
277
|
|
|
278
|
+
function printStruct(_struct: ContractStruct): Lines[] {
|
|
279
|
+
const [comments, kindedName, code] = [_struct.comments, _struct.name, _struct.variables];
|
|
280
|
+
const struct: Lines[] = [...comments];
|
|
281
|
+
|
|
282
|
+
const braces = code.length > 0 ? '{' : '{}';
|
|
283
|
+
struct.push([`struct ${kindedName}`, braces].join(' '));
|
|
284
|
+
|
|
285
|
+
if (code.length > 0) {
|
|
286
|
+
struct.push(code, '}');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return struct;
|
|
290
|
+
}
|
|
291
|
+
|
|
261
292
|
function printArgument(arg: FunctionArgument, { transformName }: Helpers): string {
|
|
262
293
|
let type: string;
|
|
263
294
|
if (typeof arg.type === 'string') {
|
|
@@ -65,7 +65,9 @@ export function requireAccessControl(
|
|
|
65
65
|
}
|
|
66
66
|
case 'roles': {
|
|
67
67
|
const roleId = roleIdPrefix + '_ROLE';
|
|
68
|
-
const addedConstant = c.
|
|
68
|
+
const addedConstant = c.addConstantOrImmutableOrErrorDefinition(
|
|
69
|
+
`bytes32 public constant ${roleId} = keccak256("${roleId}");`,
|
|
70
|
+
);
|
|
69
71
|
if (roleOwner && addedConstant) {
|
|
70
72
|
c.addConstructorArgument({ type: 'address', name: roleOwner });
|
|
71
73
|
c.addConstructorCode(`_grantRole(${roleId}, ${roleOwner});`);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { BaseFunction, ContractBuilder, ContractStruct } from './contract';
|
|
2
|
+
import { computeNamespacedStorageSlot } from './utils/namespaced-slot';
|
|
3
|
+
import { OptionsError } from './error';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sets namespaced variables in storage struct, and adds a function to retrieve namespaced storage.
|
|
7
|
+
*/
|
|
8
|
+
export function setNamespacedStorage(c: ContractBuilder, structVariables: string[], namespacePrefix: string) {
|
|
9
|
+
validateNoWhitespace(namespacePrefix);
|
|
10
|
+
|
|
11
|
+
const namespaceId = toNamespaceId(namespacePrefix, c.name);
|
|
12
|
+
const storageFn = makeStorageFunction(c.name);
|
|
13
|
+
const storageStruct = makeStorageStruct(c.name, namespaceId);
|
|
14
|
+
const namespacedStorageConstant = `${c.name.toUpperCase()}_STORAGE_LOCATION`;
|
|
15
|
+
|
|
16
|
+
structVariables.forEach(v => c.addStructVariable(storageStruct, v));
|
|
17
|
+
|
|
18
|
+
c.addConstantOrImmutableOrErrorDefinition(
|
|
19
|
+
`bytes32 private constant ${namespacedStorageConstant} = ${computeNamespacedStorageSlot(namespaceId)};`,
|
|
20
|
+
[`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`],
|
|
21
|
+
);
|
|
22
|
+
c.addFunctionCode(`assembly { $.slot := ${namespacedStorageConstant} }`, storageFn);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function toStorageStructInstantiation(name: string) {
|
|
26
|
+
return `${name}Storage storage $ = ${makeStorageFunction(name).name}();`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* According to ERC-7201, namespace ids should not contain any whitespace characters.
|
|
31
|
+
*/
|
|
32
|
+
function validateNoWhitespace(namespacePrefix: string) {
|
|
33
|
+
if (namespacePrefix.match(/\s+/)) {
|
|
34
|
+
throw new OptionsError({ namespacePrefix: 'Namespace prefix should not contain whitespace characters' });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a namespace ID from a namespace prefix and a contract name.
|
|
40
|
+
* If the namespace prefix is empty, returns the contract name.
|
|
41
|
+
*/
|
|
42
|
+
function toNamespaceId(namespacePrefix: string, name: string) {
|
|
43
|
+
if (namespacePrefix.length === 0) {
|
|
44
|
+
return name;
|
|
45
|
+
} else {
|
|
46
|
+
return `${namespacePrefix}.${name}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function makeStorageFunction(name: string): BaseFunction {
|
|
51
|
+
const fn: BaseFunction = {
|
|
52
|
+
name: `_get${name}Storage`,
|
|
53
|
+
kind: 'private' as const,
|
|
54
|
+
mutability: 'pure',
|
|
55
|
+
args: [],
|
|
56
|
+
returns: [`${name}Storage storage $`],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return fn;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function makeStorageStruct(name: string, namespaceId: string) {
|
|
63
|
+
const struct: ContractStruct = {
|
|
64
|
+
name: `${name}Storage`,
|
|
65
|
+
comments: [`/// @custom:storage-location erc7201:${namespaceId}`],
|
|
66
|
+
variables: [],
|
|
67
|
+
};
|
|
68
|
+
return struct;
|
|
69
|
+
}
|
package/src/stablecoin.ts
CHANGED
|
@@ -107,7 +107,9 @@ function addCustodian(c: ContractBuilder, access: Access) {
|
|
|
107
107
|
case 'roles': {
|
|
108
108
|
const roleOwner = 'custodian';
|
|
109
109
|
const roleId = 'CUSTODIAN_ROLE';
|
|
110
|
-
const addedConstant = c.
|
|
110
|
+
const addedConstant = c.addConstantOrImmutableOrErrorDefinition(
|
|
111
|
+
`bytes32 public constant ${roleId} = keccak256("${roleId}");`,
|
|
112
|
+
);
|
|
111
113
|
if (roleOwner && addedConstant) {
|
|
112
114
|
c.addConstructorArgument({ type: 'address', name: roleOwner });
|
|
113
115
|
c.addConstructorCode(`_grantRole(${roleId}, ${roleOwner});`);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { keccak256 } from 'ethereum-cryptography/keccak';
|
|
2
|
+
import { hexToBytes, toHex, utf8ToBytes } from 'ethereum-cryptography/utils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the ERC-7201 storage location for a given namespace id
|
|
6
|
+
*/
|
|
7
|
+
export function computeNamespacedStorageSlot(id: string): string {
|
|
8
|
+
const innerHash = keccak256(utf8ToBytes(id));
|
|
9
|
+
const minusOne = BigInt('0x' + toHex(innerHash)) - 1n;
|
|
10
|
+
const minusOneBytes = hexToBytes(minusOne.toString(16).padStart(64, '0'));
|
|
11
|
+
|
|
12
|
+
const outerHash = keccak256(minusOneBytes);
|
|
13
|
+
|
|
14
|
+
const mask = BigInt('0xff');
|
|
15
|
+
const masked = BigInt('0x' + toHex(outerHash)) & ~mask;
|
|
16
|
+
|
|
17
|
+
return '0x' + masked.toString(16).padStart(64, '0');
|
|
18
|
+
}
|