@openzeppelin/wizard 0.8.1 → 0.10.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 +9 -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/environments/hardhat/package-lock.json +64 -64
- package/dist/environments/hardhat/package.json +2 -2
- package/dist/environments/hardhat/upgradeable/package-lock.json +69 -69
- package/dist/environments/hardhat/upgradeable/package.json +4 -4
- 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/account.js +1 -1
- package/dist/generate/account.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 +3 -1
- 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 +28 -7
- 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/set-upgradeable.d.ts.map +1 -1
- package/dist/set-upgradeable.js +2 -0
- package/dist/set-upgradeable.js.map +1 -1
- package/dist/signer.d.ts +4 -4
- package/dist/signer.js +7 -7
- package/dist/signer.js.map +1 -1
- package/dist/stablecoin.d.ts +1 -1
- package/dist/stablecoin.d.ts.map +1 -1
- package/dist/stablecoin.js +29 -21
- 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/utils/version.d.ts +1 -1
- package/dist/utils/version.js +1 -1
- package/dist/zip-foundry.js +4 -4
- package/dist/zip-foundry.js.map +1 -1
- package/dist/zip-hardhat.d.ts.map +1 -1
- package/dist/zip-hardhat.js +1 -0
- package/dist/zip-hardhat.js.map +1 -1
- package/package.json +7 -4
- package/src/account.ts +1 -0
- package/src/contract.ts +64 -9
- package/src/environments/hardhat/package-lock.json +64 -64
- package/src/environments/hardhat/package.json +3 -3
- package/src/environments/hardhat/upgradeable/package-lock.json +69 -69
- package/src/environments/hardhat/upgradeable/package.json +5 -5
- package/src/erc1155.ts +1 -3
- package/src/erc20.ts +43 -14
- package/src/erc721.ts +25 -9
- package/src/generate/account.ts +1 -1
- package/src/generate/erc20.ts +1 -0
- package/src/generate/erc721.ts +1 -0
- package/src/generate/stablecoin.ts +3 -1
- package/src/print.ts +41 -6
- package/src/set-access-control.ts +3 -1
- package/src/set-namespaced-storage.ts +69 -0
- package/src/set-upgradeable.ts +2 -0
- package/src/signer.ts +8 -8
- package/src/stablecoin.ts +31 -18
- package/src/utils/namespaced-slot.ts +18 -0
- package/src/utils/version.ts +1 -1
- package/src/zip-hardhat.ts +1 -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/account.ts
CHANGED
|
@@ -8,7 +8,7 @@ const account = {
|
|
|
8
8
|
signatureValidation: [false, 'ERC1271', 'ERC7739'] as const,
|
|
9
9
|
ERC721Holder: [false, true] as const,
|
|
10
10
|
ERC1155Holder: [false, true] as const,
|
|
11
|
-
signer: ['
|
|
11
|
+
signer: ['ECDSA', 'EIP7702', 'P256', 'RSA', 'Multisig', 'MultisigWeighted'] as const,
|
|
12
12
|
batchedExecution: [false, true] as const,
|
|
13
13
|
ERC7579Modules: [false, 'AccountERC7579', 'AccountERC7579Hooked'] as const,
|
|
14
14
|
access: [false] as const,
|
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,10 +39,11 @@ 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 = {
|
|
44
|
-
|
|
46
|
+
restrictions: [false, 'allowlist', 'blocklist'] as const,
|
|
45
47
|
custodian: booleans,
|
|
46
48
|
upgradeable: [false] as const,
|
|
47
49
|
};
|
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)) {
|
|
@@ -84,16 +101,20 @@ function printConstructor(contract: Contract, helpers: Helpers): Lines[] {
|
|
|
84
101
|
if (hasParentParams || hasConstructorCode || (helpers.upgradeable && parentsWithInitializers.length > 0)) {
|
|
85
102
|
if (helpers.upgradeable) {
|
|
86
103
|
const upgradeableParents = parentsWithInitializers.filter(p => inferTranspiled(p.contract));
|
|
87
|
-
|
|
104
|
+
// Omit Initializable and UUPSUpgradeable since they don't have explicit constructors
|
|
105
|
+
const nonUpgradeableParentsWithConstructors = contract.parents.filter(
|
|
106
|
+
p =>
|
|
107
|
+
!inferTranspiled(p.contract) && p.contract.name !== 'Initializable' && p.contract.name !== 'UUPSUpgradeable',
|
|
108
|
+
);
|
|
88
109
|
const constructor = printFunction2(
|
|
89
110
|
[
|
|
90
|
-
|
|
111
|
+
nonUpgradeableParentsWithConstructors.length > 0
|
|
91
112
|
? '/// @custom:oz-upgrades-unsafe-allow-reachable constructor'
|
|
92
113
|
: '/// @custom:oz-upgrades-unsafe-allow constructor',
|
|
93
114
|
],
|
|
94
115
|
'constructor',
|
|
95
116
|
[],
|
|
96
|
-
|
|
117
|
+
nonUpgradeableParentsWithConstructors.flatMap(p => printParentConstructor(p, helpers)),
|
|
97
118
|
['_disableInitializers();'],
|
|
98
119
|
);
|
|
99
120
|
const initializer = printFunction2(
|
|
@@ -258,6 +279,20 @@ function printFunction2(
|
|
|
258
279
|
return fn;
|
|
259
280
|
}
|
|
260
281
|
|
|
282
|
+
function printStruct(_struct: ContractStruct): Lines[] {
|
|
283
|
+
const [comments, kindedName, code] = [_struct.comments, _struct.name, _struct.variables];
|
|
284
|
+
const struct: Lines[] = [...comments];
|
|
285
|
+
|
|
286
|
+
const braces = code.length > 0 ? '{' : '{}';
|
|
287
|
+
struct.push([`struct ${kindedName}`, braces].join(' '));
|
|
288
|
+
|
|
289
|
+
if (code.length > 0) {
|
|
290
|
+
struct.push(code, '}');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return struct;
|
|
294
|
+
}
|
|
295
|
+
|
|
261
296
|
function printArgument(arg: FunctionArgument, { transformName }: Helpers): string {
|
|
262
297
|
let type: string;
|
|
263
298
|
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/set-upgradeable.ts
CHANGED
|
@@ -21,6 +21,7 @@ function setUpgradeableBase(
|
|
|
21
21
|
c.addParent({
|
|
22
22
|
name: 'Initializable',
|
|
23
23
|
path: '@openzeppelin/contracts/proxy/utils/Initializable.sol',
|
|
24
|
+
transpiled: false,
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
switch (upgradeable) {
|
|
@@ -32,6 +33,7 @@ function setUpgradeableBase(
|
|
|
32
33
|
const UUPSUpgradeable = {
|
|
33
34
|
name: 'UUPSUpgradeable',
|
|
34
35
|
path: '@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol',
|
|
36
|
+
transpiled: false,
|
|
35
37
|
};
|
|
36
38
|
c.addParent(UUPSUpgradeable);
|
|
37
39
|
c.addOverride(UUPSUpgradeable, functions._authorizeUpgrade);
|
package/src/signer.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { OptionsError } from './error';
|
|
|
3
3
|
import type { Upgradeable } from './set-upgradeable';
|
|
4
4
|
import { defineFunctions } from './utils/define-functions';
|
|
5
5
|
|
|
6
|
-
export const SignerOptions = [false, '
|
|
6
|
+
export const SignerOptions = [false, 'ECDSA', 'EIP7702', 'P256', 'RSA', 'Multisig', 'MultisigWeighted'] as const;
|
|
7
7
|
export type SignerOptions = (typeof SignerOptions)[number];
|
|
8
8
|
|
|
9
9
|
export function addSigner(c: ContractBuilder, signer: SignerOptions, upgradeable: Upgradeable): void {
|
|
@@ -13,11 +13,11 @@ export function addSigner(c: ContractBuilder, signer: SignerOptions, upgradeable
|
|
|
13
13
|
c.addOverride({ name: signerName }, signerFunctions._rawSignatureValidation);
|
|
14
14
|
|
|
15
15
|
switch (signer) {
|
|
16
|
-
case '
|
|
16
|
+
case 'EIP7702':
|
|
17
17
|
c.addParent(signers[signer]);
|
|
18
18
|
if (upgradeable) {
|
|
19
19
|
throw new OptionsError({
|
|
20
|
-
|
|
20
|
+
eip7702: 'EOAs can upgrade by redelegating to a new account',
|
|
21
21
|
upgradeable: 'EOAs can upgrade by redelegating to a new account',
|
|
22
22
|
});
|
|
23
23
|
}
|
|
@@ -38,14 +38,14 @@ export function addSigner(c: ContractBuilder, signer: SignerOptions, upgradeable
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export const signers = {
|
|
41
|
-
ERC7702: {
|
|
42
|
-
name: 'SignerERC7702',
|
|
43
|
-
path: '@openzeppelin/contracts/utils/cryptography/signers/SignerERC7702.sol',
|
|
44
|
-
},
|
|
45
41
|
ECDSA: {
|
|
46
42
|
name: 'SignerECDSA',
|
|
47
43
|
path: '@openzeppelin/contracts/utils/cryptography/signers/SignerECDSA.sol',
|
|
48
44
|
},
|
|
45
|
+
EIP7702: {
|
|
46
|
+
name: 'SignerEIP7702',
|
|
47
|
+
path: '@openzeppelin/contracts/utils/cryptography/signers/SignerEIP7702.sol',
|
|
48
|
+
},
|
|
49
49
|
P256: {
|
|
50
50
|
name: 'SignerP256',
|
|
51
51
|
path: '@openzeppelin/contracts/utils/cryptography/signers/SignerP256.sol',
|
|
@@ -64,7 +64,7 @@ export const signers = {
|
|
|
64
64
|
},
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
export const signerArgs: Record<Exclude<SignerOptions, false | '
|
|
67
|
+
export const signerArgs: Record<Exclude<SignerOptions, false | 'EIP7702'>, { name: string; type: string }[]> = {
|
|
68
68
|
ECDSA: [{ name: 'signer', type: 'address' }],
|
|
69
69
|
P256: [
|
|
70
70
|
{ name: 'qx', type: 'bytes32' },
|
package/src/stablecoin.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from './erc20';
|
|
13
13
|
|
|
14
14
|
export interface StablecoinOptions extends ERC20Options {
|
|
15
|
-
|
|
15
|
+
restrictions?: false | 'allowlist' | 'blocklist';
|
|
16
16
|
custodian?: boolean;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -20,7 +20,7 @@ export const defaults: Required<StablecoinOptions> = {
|
|
|
20
20
|
...erc20defaults,
|
|
21
21
|
name: 'MyStablecoin',
|
|
22
22
|
symbol: 'MST',
|
|
23
|
-
|
|
23
|
+
restrictions: false,
|
|
24
24
|
custodian: false,
|
|
25
25
|
} as const;
|
|
26
26
|
|
|
@@ -29,7 +29,7 @@ function withDefaults(opts: StablecoinOptions): Required<StablecoinOptions> {
|
|
|
29
29
|
...withERC20Defaults(opts),
|
|
30
30
|
name: opts.name ?? defaults.name,
|
|
31
31
|
symbol: opts.symbol ?? defaults.symbol,
|
|
32
|
-
|
|
32
|
+
restrictions: opts.restrictions ?? defaults.restrictions,
|
|
33
33
|
custodian: opts.custodian ?? defaults.custodian,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
@@ -39,7 +39,7 @@ export function printStablecoin(opts: StablecoinOptions = defaults): string {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export function isAccessControlRequired(opts: Partial<StablecoinOptions>): boolean {
|
|
42
|
-
return opts.mintable || opts.
|
|
42
|
+
return opts.mintable || opts.restrictions !== false || opts.custodian || opts.pausable || opts.upgradeable === 'uups';
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function buildStablecoin(opts: StablecoinOptions): Contract {
|
|
@@ -54,33 +54,37 @@ export function buildStablecoin(opts: StablecoinOptions): Contract {
|
|
|
54
54
|
addCustodian(c, allOpts.access);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
if (allOpts.
|
|
58
|
-
|
|
57
|
+
if (allOpts.restrictions) {
|
|
58
|
+
addRestrictions(c, allOpts.access, allOpts.restrictions);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
return c;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
name:
|
|
68
|
-
path: `@openzeppelin/community-contracts/token/ERC20/extensions
|
|
64
|
+
function addRestrictions(c: ContractBuilder, access: Access, mode: 'allowlist' | 'blocklist') {
|
|
65
|
+
const isAllowlist = mode === 'allowlist';
|
|
66
|
+
const ERC20Restricted = {
|
|
67
|
+
name: 'ERC20Restricted',
|
|
68
|
+
path: `@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Restricted.sol`,
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
c.addParent(
|
|
72
|
-
c.addOverride(
|
|
73
|
-
c.addOverride(ERC20Limitation, functions._approve);
|
|
71
|
+
c.addParent(ERC20Restricted);
|
|
72
|
+
c.addOverride(ERC20Restricted, functions._update);
|
|
74
73
|
|
|
75
|
-
|
|
74
|
+
if (isAllowlist) {
|
|
75
|
+
c.addOverride(ERC20Restricted, functions.isUserAllowed);
|
|
76
|
+
c.setFunctionBody([`return getRestriction(user) == Restriction.ALLOWED;`], functions.isUserAllowed);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const [addFn, removeFn] = isAllowlist
|
|
76
80
|
? [functions.allowUser, functions.disallowUser]
|
|
77
81
|
: [functions.blockUser, functions.unblockUser];
|
|
78
82
|
|
|
79
83
|
requireAccessControl(c, addFn, access, 'LIMITER', 'limiter');
|
|
80
|
-
c.addFunctionCode(`_${
|
|
84
|
+
c.addFunctionCode(`_${isAllowlist ? 'allow' : 'block'}User(user);`, addFn);
|
|
81
85
|
|
|
82
86
|
requireAccessControl(c, removeFn, access, 'LIMITER', 'limiter');
|
|
83
|
-
c.addFunctionCode(`
|
|
87
|
+
c.addFunctionCode(`_resetUser(user);`, removeFn);
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
function addCustodian(c: ContractBuilder, access: Access) {
|
|
@@ -107,7 +111,9 @@ function addCustodian(c: ContractBuilder, access: Access) {
|
|
|
107
111
|
case 'roles': {
|
|
108
112
|
const roleOwner = 'custodian';
|
|
109
113
|
const roleId = 'CUSTODIAN_ROLE';
|
|
110
|
-
const addedConstant = c.
|
|
114
|
+
const addedConstant = c.addConstantOrImmutableOrErrorDefinition(
|
|
115
|
+
`bytes32 public constant ${roleId} = keccak256("${roleId}");`,
|
|
116
|
+
);
|
|
111
117
|
if (roleOwner && addedConstant) {
|
|
112
118
|
c.addConstructorArgument({ type: 'address', name: roleOwner });
|
|
113
119
|
c.addConstructorCode(`_grantRole(${roleId}, ${roleOwner});`);
|
|
@@ -159,5 +165,12 @@ const functions = {
|
|
|
159
165
|
kind: 'public' as const,
|
|
160
166
|
args: [{ name: 'user', type: 'address' }],
|
|
161
167
|
},
|
|
168
|
+
|
|
169
|
+
isUserAllowed: {
|
|
170
|
+
kind: 'public' as const,
|
|
171
|
+
args: [{ name: 'user', type: 'address' }],
|
|
172
|
+
returns: ['bool'],
|
|
173
|
+
mutability: 'view' as const,
|
|
174
|
+
},
|
|
162
175
|
}),
|
|
163
176
|
};
|
|
@@ -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
|
+
}
|
package/src/utils/version.ts
CHANGED