Skip to content

Commit b9d58dc

Browse files
Amxxericglau
andauthored
Unify upgradeability of Account with the other types of contract (#658)
Co-authored-by: Eric Lau <[email protected]>
1 parent 029790c commit b9d58dc

24 files changed

+789
-1429
lines changed

.changeset/lovely-groups-run.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@openzeppelin/wizard': patch
3+
---
4+
5+
Remove all initializers from non-upgradeable accounts.

packages/core/solidity/src/account.test.ts.md

Lines changed: 478 additions & 984 deletions
Large diffs are not rendered by default.
-338 Bytes
Binary file not shown.

packages/core/solidity/src/account.ts

Lines changed: 32 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,10 @@ import { ContractBuilder } from './contract';
22
import type { Contract } from './contract';
33
import { defineFunctions } from './utils/define-functions';
44
import { printContract } from './print';
5-
import { makeUpgradeable } from './helpers';
65
import { defaults as commonDefaults, withCommonDefaults, type CommonOptions } from './common-options';
6+
import { upgradeableName } from './options';
77
import { setInfo } from './set-info';
8-
import {
9-
addLockingConstructorAllowReachable,
10-
addSigner,
11-
signerArgs,
12-
signerFunctions,
13-
signers,
14-
type SignerOptions,
15-
} from './signer';
8+
import { addSigner, signerFunctions, signers, type SignerOptions } from './signer';
169
import { setUpgradeableAccount } from './set-upgradeable';
1710

1811
export const defaults: Required<AccountOptions> = {
@@ -75,6 +68,7 @@ export function buildAccount(opts: AccountOptions): Contract {
7568
c.addImportOnly({
7669
name: 'PackedUserOperation',
7770
path: '@openzeppelin/contracts/interfaces/draft-IERC4337.sol',
71+
transpiled: false, // PackedUserOperation doesn't start with "I" so its not recognized as an "interface object"
7872
});
7973
}
8074

@@ -86,16 +80,16 @@ function addParents(c: ContractBuilder, opts: AccountOptions): void {
8680
c.addParent({
8781
name: 'Account',
8882
path: `@openzeppelin/contracts/account/Account.sol`,
83+
transpiled: false,
8984
});
90-
c.addOverride({ name: 'Account' }, functions._validateUserOp);
85+
c.addOverride({ name: 'Account', transpiled: false }, functions._validateUserOp);
9186

9287
if (opts.signatureValidation === 'ERC7739') addEIP712(c, opts);
9388

9489
// Extensions
9590
addSignatureValidation(c, opts);
9691
addERC7579Modules(c, opts);
9792
addSigner(c, opts.signer ?? false, opts.upgradeable ?? false);
98-
addSignerInitializer(c, opts);
9993
addMultisigFunctions(c, opts);
10094
addBatchedExecution(c, opts);
10195
addERC721Holder(c, opts);
@@ -108,6 +102,7 @@ function addSignatureValidation(c: ContractBuilder, opts: AccountOptions) {
108102
c.addParent({
109103
name: 'ERC7739',
110104
path: '@openzeppelin/contracts/utils/cryptography/signers/draft-ERC7739.sol',
105+
transpiled: false,
111106
});
112107
break;
113108
case 'ERC1271':
@@ -133,6 +128,7 @@ function addERC721Holder(c: ContractBuilder, opts: AccountOptions): void {
133128
c.addParent({
134129
name: 'ERC721Holder',
135130
path: '@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol',
131+
transpiled: false,
136132
});
137133
}
138134

@@ -141,6 +137,7 @@ function addERC1155Holder(c: ContractBuilder, opts: AccountOptions): void {
141137
c.addParent({
142138
name: 'ERC1155Holder',
143139
path: '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol',
140+
transpiled: false,
144141
});
145142
}
146143

@@ -150,8 +147,9 @@ function addBatchedExecution(c: ContractBuilder, opts: AccountOptions): void {
150147
c.addParent({
151148
name: 'ERC7821',
152149
path: '@openzeppelin/contracts/account/extensions/draft-ERC7821.sol',
150+
transpiled: false,
153151
});
154-
c.addOverride({ name: 'ERC7821' }, functions._erc7821AuthorizedExecutor);
152+
c.addOverride({ name: 'ERC7821', transpiled: false }, functions._erc7821AuthorizedExecutor);
155153
c.setFunctionBody(
156154
['return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);'],
157155
functions._erc7821AuthorizedExecutor,
@@ -161,112 +159,41 @@ function addBatchedExecution(c: ContractBuilder, opts: AccountOptions): void {
161159
function addERC7579Modules(c: ContractBuilder, opts: AccountOptions): void {
162160
if (!opts.ERC7579Modules) return;
163161

164-
// Base AccountERC7579 account (upgradeable or not)
165-
const name = makeUpgradeable('AccountERC7579', opts.upgradeable);
166-
167162
c.addParent({
168-
name: makeUpgradeable(opts.ERC7579Modules, opts.upgradeable),
169-
path: makeUpgradeable(
170-
`@openzeppelin/contracts/account/extensions/draft-${opts.ERC7579Modules}.sol`,
171-
opts.upgradeable,
172-
),
163+
name: opts.ERC7579Modules,
164+
path: `@openzeppelin/contracts/account/extensions/draft-${opts.ERC7579Modules}.sol`,
173165
});
174166
if (opts.ERC7579Modules !== 'AccountERC7579') {
175167
c.addImportOnly({
176-
name: makeUpgradeable('AccountERC7579', opts.upgradeable),
177-
path: makeUpgradeable('@openzeppelin/contracts/account/extensions/draft-AccountERC7579.sol', opts.upgradeable),
168+
name: 'AccountERC7579',
169+
path: '@openzeppelin/contracts/account/extensions/draft-AccountERC7579.sol',
178170
});
179171
}
180172

181173
// Accounts that use ERC7579 without a signer must be constructed with at least one module (executor of validation)
182174
if (!opts.signer) {
183-
const args = [
184-
{ type: 'uint256', name: 'moduleTypeId' },
185-
{ type: 'address', name: 'module' },
186-
{ type: 'bytes calldata', name: 'initData' },
187-
];
188-
const body = [
189-
'require(moduleTypeId == MODULE_TYPE_VALIDATOR || moduleTypeId == MODULE_TYPE_EXECUTOR);',
190-
'_installModule(moduleTypeId, module, initData);',
191-
];
192-
if (opts.upgradeable) {
193-
c.addParent({
194-
name: 'Initializable',
195-
path: '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol',
196-
});
197-
addLockingConstructorAllowReachable(c);
198-
199-
const fn = { name: 'initialize', kind: 'public' as const, args };
200-
c.addModifier('initializer', fn);
201-
c.setFunctionBody(body, fn);
202-
} else {
203-
for (const arg of args) c.addConstructorArgument(arg);
204-
for (const line of body) c.addConstructorCode(line);
205-
}
175+
c.addConstructorArgument({ type: 'uint256', name: 'moduleTypeId' });
176+
c.addConstructorArgument({ type: 'address', name: 'module' });
177+
c.addConstructorArgument({ type: 'bytes calldata', name: 'initData' });
178+
c.addConstructorCode('require(moduleTypeId == MODULE_TYPE_VALIDATOR || moduleTypeId == MODULE_TYPE_EXECUTOR);');
179+
c.addConstructorCode('_installModule(moduleTypeId, module, initData);');
206180
}
207181

208-
// isValidSignature override
209-
c.addOverride({ name }, functions.isValidSignature);
182+
c.addOverride({ name: 'AccountERC7579' }, functions._validateUserOp);
183+
c.addOverride({ name: 'AccountERC7579' }, functions.isValidSignature);
184+
210185
if (opts.signatureValidation === 'ERC7739') {
211-
c.addOverride({ name: 'ERC7739' }, functions.isValidSignature);
186+
c.addOverride({ name: 'ERC7739', transpiled: false }, functions.isValidSignature);
212187
c.setFunctionBody(
213188
[
214189
'// ERC-7739 can return the ERC-1271 magic value, 0xffffffff (invalid) or 0x77390001 (detection).',
215190
'// If the returned value is 0xffffffff, fallback to ERC-7579 validation.',
216191
'bytes4 erc7739magic = ERC7739.isValidSignature(hash, signature);',
217-
`return erc7739magic == bytes4(0xffffffff) ? ${name}.isValidSignature(hash, signature) : erc7739magic;`,
192+
`return erc7739magic == bytes4(0xffffffff) ? ${opts.upgradeable ? upgradeableName('AccountERC7579') : 'AccountERC7579'}.isValidSignature(hash, signature) : erc7739magic;`,
218193
],
219194
functions.isValidSignature,
220195
);
221196
}
222-
223-
// _validateUserOp override
224-
c.addOverride({ name }, functions._validateUserOp);
225-
}
226-
227-
function addSignerInitializer(c: ContractBuilder, opts: AccountOptions): void {
228-
if (opts.upgradeable) {
229-
if (!opts.signer) {
230-
addLockingConstructorAllowReachable(c);
231-
}
232-
return; // Initializer added in signer.ts
233-
}
234-
if (!opts.signer || opts.signer === 'ERC7702') return; // No initialization required
235-
236-
c.addParent({
237-
name: 'Initializable',
238-
path: opts.upgradeable
239-
? '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'
240-
: '@openzeppelin/contracts/proxy/utils/Initializable.sol',
241-
});
242-
243-
addLockingConstructorAllowReachable(c, [
244-
'// Accounts are typically deployed and initialized as clones during their first user op,',
245-
'// therefore, initializers are disabled for the implementation contract',
246-
]);
247-
248-
const fn = { name: 'initialize', kind: 'public' as const, args: signerArgs[opts.signer] };
249-
c.addModifier('initializer', fn);
250-
251-
switch (opts.signer) {
252-
case 'Multisig':
253-
c.addFunctionCode(`_addSigners(${signerArgs[opts.signer][0]!.name});`, fn);
254-
c.addFunctionCode(`_setThreshold(${signerArgs[opts.signer][1]!.name});`, fn);
255-
break;
256-
case 'MultisigWeighted':
257-
c.addFunctionCode(`_addSigners(${signerArgs[opts.signer][0]!.name});`, fn);
258-
c.addFunctionCode(
259-
`_setSignerWeights(${signerArgs[opts.signer][0]!.name}, ${signerArgs[opts.signer][1]!.name});`,
260-
fn,
261-
);
262-
c.addFunctionCode(`_setThreshold(${signerArgs[opts.signer][2]!.name});`, fn);
263-
break;
264-
case 'ECDSA':
265-
case 'P256':
266-
case 'RSA':
267-
c.addFunctionCode(`_setSigner(${signerArgs[opts.signer].map(({ name }) => name).join(', ')});`, fn);
268-
break;
269-
}
270197
}
271198

272199
function addMultisigFunctions(c: ContractBuilder, opts: AccountOptions): void {
@@ -296,6 +223,7 @@ function addEIP712(c: ContractBuilder, opts: AccountOptions): void {
296223
{
297224
name: 'EIP712',
298225
path: '@openzeppelin/contracts/utils/cryptography/EIP712.sol',
226+
transpiled: false, // do not use the upgradeable variant for in Accounts
299227
},
300228
[opts.name, '1'],
301229
);
@@ -309,21 +237,22 @@ function overrideRawSignatureValidation(c: ContractBuilder, opts: AccountOptions
309237
// to provide a custom validation logic
310238
if (!opts.signer && !opts.ERC7579Modules) {
311239
// Custom validation logic
312-
c.addOverride({ name: 'Account' }, signerFunctions._rawSignatureValidation);
240+
c.addOverride({ name: 'Account', transpiled: false }, signerFunctions._rawSignatureValidation);
313241
c.setFunctionBody(['// Custom validation logic', 'return false;'], signerFunctions._rawSignatureValidation);
314242
}
315243

316244
// Disambiguate between Signer and AccountERC7579
317245
if (opts.signer && opts.ERC7579Modules) {
318-
const accountName = makeUpgradeable('AccountERC7579', opts.upgradeable);
319-
const signerName = makeUpgradeable(`Signer${opts.signer}`, opts.upgradeable);
246+
const accountName = opts.upgradeable ? upgradeableName('AccountERC7579') : 'AccountERC7579';
247+
const signerName = opts.upgradeable ? upgradeableName(`Signer${opts.signer}`) : `Signer${opts.signer}`;
320248

321249
c.addImportOnly({
322250
name: 'AbstractSigner',
323251
path: '@openzeppelin/contracts/utils/cryptography/signers/AbstractSigner.sol',
252+
transpiled: false,
324253
});
325-
c.addOverride({ name: 'AbstractSigner' }, signerFunctions._rawSignatureValidation);
326-
c.addOverride({ name: accountName }, signerFunctions._rawSignatureValidation);
254+
c.addOverride({ name: 'AbstractSigner', transpiled: false }, signerFunctions._rawSignatureValidation);
255+
c.addOverride({ name: 'AccountERC7579' }, signerFunctions._rawSignatureValidation);
327256
c.setFunctionComments(
328257
[
329258
`// IMPORTANT: Make sure ${signerName} is most derived than ${accountName}`,
@@ -336,10 +265,7 @@ function overrideRawSignatureValidation(c: ContractBuilder, opts: AccountOptions
336265

337266
// Base override for `_rawSignatureValidation` given MultiSignerERC7913Weighted is MultiSignerERC7913
338267
if (opts.signer === 'MultisigWeighted') {
339-
c.addImportOnly({
340-
name: makeUpgradeable(signers.Multisig.name, opts.upgradeable),
341-
path: makeUpgradeable(signers.Multisig.path, opts.upgradeable),
342-
});
268+
c.addImportOnly(signers.Multisig);
343269
}
344270
}
345271
}

packages/core/solidity/src/contract.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@ export interface Contract {
99
functions: ContractFunction[];
1010
constructorCode: string[];
1111
constructorArgs: FunctionArgument[];
12-
constructorComments: string[];
1312
variables: string[];
14-
shouldAutoTranspileImports: boolean;
15-
shouldInstallContractsUpgradeable: boolean;
16-
shouldUseUpgradesPluginsForProxyDeployment: boolean;
13+
upgradeable: boolean;
1714
}
1815

1916
export type Value = string | number | { lit: string } | { note: string; value: Value };
@@ -78,17 +75,13 @@ export interface NatspecTag {
7875
export class ContractBuilder implements Contract {
7976
readonly name: string;
8077
license: string = 'MIT';
81-
82-
shouldAutoTranspileImports: boolean = false;
83-
shouldInstallContractsUpgradeable: boolean = false;
84-
shouldUseUpgradesPluginsForProxyDeployment: boolean = false;
78+
upgradeable = false;
8579

8680
readonly using: Using[] = [];
8781
readonly natspecTags: NatspecTag[] = [];
8882

8983
readonly constructorArgs: FunctionArgument[] = [];
9084
readonly constructorCode: string[] = [];
91-
readonly constructorComments: string[] = [];
9285
readonly variableSet: Set<string> = new Set();
9386

9487
private parentMap: Map<string, Parent> = new Map<string, Parent>();
@@ -187,15 +180,6 @@ export class ContractBuilder implements Contract {
187180
this.constructorCode.push(code);
188181
}
189182

190-
addConstructorComment(comment: string) {
191-
if (this.shouldAutoTranspileImports) {
192-
throw new Error(
193-
'Constructor comments are not supported when `shouldAutoTranspileImports` is true, since constructor will be transformed into an initializer',
194-
);
195-
}
196-
this.constructorComments.push(comment);
197-
}
198-
199183
addFunctionCode(code: string, baseFn: BaseFunction, mutability?: FunctionMutability) {
200184
const fn = this.addFunction(baseFn);
201185
if (fn.final) {

packages/core/solidity/src/erc1155.test.ts.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,8 @@ Generated by [AVA](https://avajs.dev).
377377
}␊
378378
379379
function initialize(address defaultAdmin, address pauser, address minter)␊
380-
public initializer␊
380+
public␊
381+
initializer␊
381382
{␊
382383
__ERC1155_init("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊
383384
__AccessControl_init();␊
@@ -462,7 +463,8 @@ Generated by [AVA](https://avajs.dev).
462463
}␊
463464
464465
function initialize(address defaultAdmin, address pauser, address minter, address upgrader)␊
465-
public initializer␊
466+
public␊
467+
initializer␊
466468
{␊
467469
__ERC1155_init("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊
468470
__AccessControl_init();␊
4 Bytes
Binary file not shown.

packages/core/solidity/src/erc20.test.ts.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,8 @@ Generated by [AVA](https://avajs.dev).
10491049
}␊
10501050
10511051
function initialize(address defaultAdmin, address tokenBridge, address recipient, address pauser, address minter, address upgrader)␊
1052-
public initializer␊
1052+
public␊
1053+
initializer␊
10531054
{␊
10541055
__ERC20_init("MyToken", "MTK");␊
10551056
__ERC20Bridgeable_init();␊
@@ -1152,7 +1153,8 @@ Generated by [AVA](https://avajs.dev).
11521153
}␊
11531154
11541155
function initialize(address recipient, address defaultAdmin, address pauser, address minter)␊
1155-
public initializer␊
1156+
public␊
1157+
initializer␊
11561158
{␊
11571159
__ERC20_init("MyToken", "MTK");␊
11581160
__ERC20Burnable_init();␊
@@ -1241,7 +1243,8 @@ Generated by [AVA](https://avajs.dev).
12411243
}␊
12421244
12431245
function initialize(address recipient, address defaultAdmin, address pauser, address minter, address upgrader)␊
1244-
public initializer␊
1246+
public␊
1247+
initializer␊
12451248
{␊
12461249
__ERC20_init("MyToken", "MTK");␊
12471250
__ERC20Burnable_init();␊
@@ -1334,7 +1337,8 @@ Generated by [AVA](https://avajs.dev).
13341337
}␊
13351338
13361339
function initialize(address recipient, address initialAuthority)␊
1337-
public initializer␊
1340+
public␊
1341+
initializer␊
13381342
{␊
13391343
__ERC20_init("MyToken", "MTK");␊
13401344
__ERC20Burnable_init();␊
4 Bytes
Binary file not shown.

packages/core/solidity/src/governor.test.ts.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,8 @@ Generated by [AVA](https://avajs.dev).
16241624
}␊
16251625
16261626
function initialize(IVotes _token, TimelockControllerUpgradeable _timelock)␊
1627-
public initializer␊
1627+
public␊
1628+
initializer␊
16281629
{␊
16291630
__Governor_init("MyGovernor");␊
16301631
__GovernorCountingSimple_init();␊

0 commit comments

Comments
 (0)