Skip to content

Commit 76b721f

Browse files
Add ERC1363 to wizard (#500)
1 parent 7ab6026 commit 76b721f

13 files changed

+126
-7
lines changed

packages/core/solidity/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Add validation for ERC20 premint field. ([#488](https://github.com/OpenZeppelin/contracts-wizard/pull/488))
6+
- Add `callback` in ERC20 features. ([#500](https://github.com/OpenZeppelin/contracts-wizard/pull/500))
67

78
## 0.5.3 (2025-03-13)
89

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ testERC20('erc20 mintable with roles', {
116116
access: 'roles',
117117
});
118118

119+
testERC20('erc20 callback', {
120+
callback: true,
121+
});
122+
119123
testERC20('erc20 permit', {
120124
permit: true,
121125
});
@@ -249,6 +253,7 @@ testERC20('erc20 full crossChainBridging custom non-upgradeable', {
249253
burnable: true,
250254
mintable: true,
251255
pausable: true,
256+
callback: true,
252257
permit: true,
253258
votes: true,
254259
flashmint: true,
@@ -262,6 +267,7 @@ testERC20('erc20 full upgradeable transparent', {
262267
burnable: true,
263268
mintable: true,
264269
pausable: true,
270+
callback: true,
265271
permit: true,
266272
votes: true,
267273
flashmint: true,
@@ -274,6 +280,7 @@ testERC20('erc20 full upgradeable uups', {
274280
burnable: true,
275281
mintable: true,
276282
pausable: true,
283+
callback: true,
277284
permit: true,
278285
votes: true,
279286
flashmint: true,
@@ -286,6 +293,7 @@ testERC20('erc20 full upgradeable uups managed', {
286293
burnable: true,
287294
mintable: true,
288295
pausable: true,
296+
callback: true,
289297
permit: true,
290298
votes: true,
291299
flashmint: true,
@@ -304,6 +312,7 @@ testAPIEquivalence('erc20 API full upgradeable', {
304312
burnable: true,
305313
mintable: true,
306314
pausable: true,
315+
callback: true,
307316
permit: true,
308317
votes: true,
309318
flashmint: true,

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

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,23 @@ Generated by [AVA](https://avajs.dev).
352352
}␊
353353
`
354354

355+
## erc20 callback
356+
357+
> Snapshot 1
358+
359+
`// SPDX-License-Identifier: MIT␊
360+
// Compatible with OpenZeppelin Contracts ^5.0.0␊
361+
pragma solidity ^0.8.22;␊
362+
363+
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
364+
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
365+
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊
366+
367+
contract MyToken is ERC20, ERC1363, ERC20Permit {␊
368+
constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}␊
369+
}␊
370+
`
371+
355372
## erc20 permit
356373

357374
> Snapshot 1
@@ -915,6 +932,7 @@ Generated by [AVA](https://avajs.dev).
915932
pragma solidity ^0.8.22;␊
916933
917934
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";␊
935+
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
918936
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
919937
import {ERC20Bridgeable} from "@openzeppelin/community-contracts/contracts/token/ERC20/extensions/ERC20Bridgeable.sol";␊
920938
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";␊
@@ -924,7 +942,7 @@ Generated by [AVA](https://avajs.dev).
924942
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";␊
925943
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";␊
926944
927-
contract MyToken is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC20Permit, ERC20Votes, ERC20FlashMint {␊
945+
contract MyToken is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC1363, ERC20Permit, ERC20Votes, ERC20FlashMint {␊
928946
bytes32 public constant TOKEN_BRIDGE_ROLE = keccak256("TOKEN_BRIDGE_ROLE");␊
929947
error Unauthorized();␊
930948
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
@@ -971,7 +989,7 @@ Generated by [AVA](https://avajs.dev).
971989
function supportsInterface(bytes4 interfaceId)␊
972990
public␊
973991
view␊
974-
override(ERC20Bridgeable, AccessControl)␊
992+
override(ERC20Bridgeable, AccessControl, ERC1363)␊
975993
returns (bool)␊
976994
{␊
977995
return super.supportsInterface(interfaceId);␊
@@ -997,6 +1015,7 @@ Generated by [AVA](https://avajs.dev).
9971015
pragma solidity ^0.8.22;␊
9981016
9991017
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";␊
1018+
import {ERC1363Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC1363Upgradeable.sol";␊
10001019
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊
10011020
import {ERC20BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊
10021021
import {ERC20FlashMintUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊
@@ -1006,7 +1025,7 @@ Generated by [AVA](https://avajs.dev).
10061025
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊
10071026
import {NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol";␊
10081027
1009-
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable {␊
1028+
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC1363Upgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable {␊
10101029
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
10111030
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");␊
10121031
@@ -1022,6 +1041,7 @@ Generated by [AVA](https://avajs.dev).
10221041
__ERC20Burnable_init();␊
10231042
__ERC20Pausable_init();␊
10241043
__AccessControl_init();␊
1044+
__ERC1363_init();␊
10251045
__ERC20Permit_init("MyToken");␊
10261046
__ERC20Votes_init();␊
10271047
__ERC20FlashMint_init();␊
@@ -1052,6 +1072,15 @@ Generated by [AVA](https://avajs.dev).
10521072
{␊
10531073
super._update(from, to, value);␊
10541074
}␊
1075+
1076+
function supportsInterface(bytes4 interfaceId)␊
1077+
public␊
1078+
view␊
1079+
override(AccessControlUpgradeable, ERC1363Upgradeable)␊
1080+
returns (bool)␊
1081+
{␊
1082+
return super.supportsInterface(interfaceId);␊
1083+
}␊
10551084
10561085
function nonces(address owner)␊
10571086
public␊
@@ -1073,6 +1102,7 @@ Generated by [AVA](https://avajs.dev).
10731102
pragma solidity ^0.8.22;␊
10741103
10751104
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";␊
1105+
import {ERC1363Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC1363Upgradeable.sol";␊
10761106
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊
10771107
import {ERC20BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊
10781108
import {ERC20FlashMintUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊
@@ -1083,7 +1113,7 @@ Generated by [AVA](https://avajs.dev).
10831113
import {NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol";␊
10841114
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊
10851115
1086-
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
1116+
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC1363Upgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
10871117
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
10881118
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");␊
10891119
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");␊
@@ -1100,6 +1130,7 @@ Generated by [AVA](https://avajs.dev).
11001130
__ERC20Burnable_init();␊
11011131
__ERC20Pausable_init();␊
11021132
__AccessControl_init();␊
1133+
__ERC1363_init();␊
11031134
__ERC20Permit_init("MyToken");␊
11041135
__ERC20Votes_init();␊
11051136
__ERC20FlashMint_init();␊
@@ -1138,6 +1169,15 @@ Generated by [AVA](https://avajs.dev).
11381169
{␊
11391170
super._update(from, to, value);␊
11401171
}␊
1172+
1173+
function supportsInterface(bytes4 interfaceId)␊
1174+
public␊
1175+
view␊
1176+
override(AccessControlUpgradeable, ERC1363Upgradeable)␊
1177+
returns (bool)␊
1178+
{␊
1179+
return super.supportsInterface(interfaceId);␊
1180+
}␊
11411181
11421182
function nonces(address owner)␊
11431183
public␊
@@ -1159,6 +1199,7 @@ Generated by [AVA](https://avajs.dev).
11591199
pragma solidity ^0.8.22;␊
11601200
11611201
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";␊
1202+
import {ERC1363Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC1363Upgradeable.sol";␊
11621203
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊
11631204
import {ERC20BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊
11641205
import {ERC20FlashMintUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊
@@ -1169,7 +1210,7 @@ Generated by [AVA](https://avajs.dev).
11691210
import {NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol";␊
11701211
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊
11711212
1172-
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessManagedUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
1213+
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessManagedUpgradeable, ERC1363Upgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
11731214
/// @custom:oz-upgrades-unsafe-allow constructor␊
11741215
constructor() {␊
11751216
_disableInitializers();␊
@@ -1182,6 +1223,7 @@ Generated by [AVA](https://avajs.dev).
11821223
__ERC20Burnable_init();␊
11831224
__ERC20Pausable_init();␊
11841225
__AccessManaged_init(initialAuthority);␊
1226+
__ERC1363_init();␊
11851227
__ERC20Permit_init("MyToken");␊
11861228
__ERC20Votes_init();␊
11871229
__ERC20FlashMint_init();␊
67 Bytes
Binary file not shown.

packages/core/solidity/src/erc20.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface ERC20Options extends CommonOptions {
2626
premint?: string;
2727
premintChainId?: string;
2828
mintable?: boolean;
29+
callback?: boolean;
2930
permit?: boolean;
3031
/**
3132
* Whether to keep track of historical balances for voting in on-chain governance, and optionally specify the clock mode.
@@ -44,6 +45,7 @@ export const defaults: Required<ERC20Options> = {
4445
premint: '0',
4546
premintChainId: '',
4647
mintable: false,
48+
callback: false,
4749
permit: true,
4850
votes: false,
4951
flashmint: false,
@@ -62,6 +64,7 @@ export function withDefaults(opts: ERC20Options): Required<ERC20Options> {
6264
premint: opts.premint || defaults.premint,
6365
premintChainId: opts.premintChainId || defaults.premintChainId,
6466
mintable: opts.mintable ?? defaults.mintable,
67+
callback: opts.callback ?? defaults.callback,
6568
permit: opts.permit ?? defaults.permit,
6669
votes: opts.votes ?? defaults.votes,
6770
flashmint: opts.flashmint ?? defaults.flashmint,
@@ -106,6 +109,10 @@ export function buildERC20(opts: ERC20Options): ContractBuilder {
106109
addMintable(c, access);
107110
}
108111

112+
if (allOpts.callback) {
113+
addCallback(c);
114+
}
115+
109116
// Note: Votes requires Permit
110117
if (allOpts.permit || allOpts.votes) {
111118
addPermit(c, allOpts.name);
@@ -247,6 +254,15 @@ function addMintable(c: ContractBuilder, access: Access) {
247254
c.addFunctionCode('_mint(to, amount);', functions.mint);
248255
}
249256

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+
250266
function addPermit(c: ContractBuilder, name: string) {
251267
const ERC20Permit = {
252268
name: 'ERC20Permit',

packages/core/solidity/src/generate/erc20.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const blueprint = {
1313
burnable: booleans,
1414
pausable: booleans,
1515
mintable: booleans,
16+
callback: booleans,
1617
permit: booleans,
1718
votes: [...booleans, ...clockModeOptions] as const,
1819
flashmint: booleans,

packages/core/solidity/src/generate/stablecoin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const erc20Basic = {
1212
burnable: [false] as const,
1313
pausable: [false] as const,
1414
mintable: [false] as const,
15+
callback: [false] as const,
1516
permit: [false] as const,
1617
votes: [false] as const,
1718
flashmint: [false] as const,
@@ -28,6 +29,7 @@ const erc20Full = {
2829
burnable: [true] as const,
2930
pausable: [true] as const,
3031
mintable: [true] as const,
32+
callback: [true] as const,
3133
permit: [true] as const,
3234
votes: ['timestamp'] as const,
3335
flashmint: [true] as const,

packages/core/solidity/src/stablecoin.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ testStablecoin('stablecoin mintable with roles', {
7878
access: 'roles',
7979
});
8080

81+
testStablecoin('stablecoin callback', {
82+
callback: true,
83+
});
84+
8185
testStablecoin('stablecoin permit', {
8286
permit: true,
8387
});
@@ -118,6 +122,7 @@ testStablecoin('stablecoin full', {
118122
burnable: true,
119123
mintable: true,
120124
pausable: true,
125+
callback: true,
121126
permit: true,
122127
votes: true,
123128
flashmint: true,
@@ -142,6 +147,7 @@ testAPIEquivalence('stablecoin API full', {
142147
burnable: true,
143148
mintable: true,
144149
pausable: true,
150+
callback: true,
145151
permit: true,
146152
votes: true,
147153
flashmint: true,

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,23 @@ Generated by [AVA](https://avajs.dev).
289289
}␊
290290
`
291291

292+
## stablecoin callback
293+
294+
> Snapshot 1
295+
296+
`// SPDX-License-Identifier: MIT␊
297+
// Compatible with OpenZeppelin Contracts ^5.0.0␊
298+
pragma solidity ^0.8.22;␊
299+
300+
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
301+
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
302+
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊
303+
304+
contract MyStablecoin is ERC20, ERC1363, ERC20Permit {␊
305+
constructor() ERC20("MyStablecoin", "MST") ERC20Permit("MyStablecoin") {}␊
306+
}␊
307+
`
308+
292309
## stablecoin permit
293310

294311
> Snapshot 1
@@ -575,6 +592,7 @@ Generated by [AVA](https://avajs.dev).
575592
pragma solidity ^0.8.22;␊
576593
577594
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";␊
595+
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
578596
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
579597
import {ERC20Allowlist} from "@openzeppelin/community-contracts/contracts/token/ERC20/extensions/ERC20Allowlist.sol";␊
580598
import {ERC20Bridgeable} from "@openzeppelin/community-contracts/contracts/token/ERC20/extensions/ERC20Bridgeable.sol";␊
@@ -586,7 +604,7 @@ Generated by [AVA](https://avajs.dev).
586604
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";␊
587605
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";␊
588606
589-
contract MyStablecoin is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC20Permit, ERC20Votes, ERC20FlashMint, ERC20Custodian, ERC20Allowlist {␊
607+
contract MyStablecoin is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC1363, ERC20Permit, ERC20Votes, ERC20FlashMint, ERC20Custodian, ERC20Allowlist {␊
590608
bytes32 public constant TOKEN_BRIDGE_ROLE = keccak256("TOKEN_BRIDGE_ROLE");␊
591609
error Unauthorized();␊
592610
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
@@ -656,7 +674,7 @@ Generated by [AVA](https://avajs.dev).
656674
function supportsInterface(bytes4 interfaceId)␊
657675
public␊
658676
view␊
659-
override(ERC20Bridgeable, AccessControl)␊
677+
override(ERC20Bridgeable, AccessControl, ERC1363)␊
660678
returns (bool)␊
661679
{␊
662680
return super.supportsInterface(interfaceId);␊
Binary file not shown.

0 commit comments

Comments
 (0)