Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/solidity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

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

## 0.5.3 (2025-03-13)

Expand Down
9 changes: 9 additions & 0 deletions packages/core/solidity/src/erc20.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ testERC20('erc20 mintable with roles', {
access: 'roles',
});

testERC20('erc20 callback', {
callback: true,
});

testERC20('erc20 permit', {
permit: true,
});
Expand Down Expand Up @@ -249,6 +253,7 @@ testERC20('erc20 full crossChainBridging custom non-upgradeable', {
burnable: true,
mintable: true,
pausable: true,
callback: true,
permit: true,
votes: true,
flashmint: true,
Expand All @@ -262,6 +267,7 @@ testERC20('erc20 full upgradeable transparent', {
burnable: true,
mintable: true,
pausable: true,
callback: true,
permit: true,
votes: true,
flashmint: true,
Expand All @@ -274,6 +280,7 @@ testERC20('erc20 full upgradeable uups', {
burnable: true,
mintable: true,
pausable: true,
callback: true,
permit: true,
votes: true,
flashmint: true,
Expand All @@ -286,6 +293,7 @@ testERC20('erc20 full upgradeable uups managed', {
burnable: true,
mintable: true,
pausable: true,
callback: true,
permit: true,
votes: true,
flashmint: true,
Expand All @@ -304,6 +312,7 @@ testAPIEquivalence('erc20 API full upgradeable', {
burnable: true,
mintable: true,
pausable: true,
callback: true,
permit: true,
votes: true,
flashmint: true,
Expand Down
52 changes: 47 additions & 5 deletions packages/core/solidity/src/erc20.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,23 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## erc20 callback

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Contracts ^5.0.0␊
pragma solidity ^0.8.22;␊
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊
contract MyToken is ERC20, ERC1363, ERC20Permit {␊
constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}␊
}␊
`

## erc20 permit

> Snapshot 1
Expand Down Expand Up @@ -915,6 +932,7 @@ Generated by [AVA](https://avajs.dev).
pragma solidity ^0.8.22;␊
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";␊
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
import {ERC20Bridgeable} from "@openzeppelin/community-contracts/contracts/token/ERC20/extensions/ERC20Bridgeable.sol";␊
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";␊
Expand All @@ -924,7 +942,7 @@ Generated by [AVA](https://avajs.dev).
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";␊
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";␊
contract MyToken is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC20Permit, ERC20Votes, ERC20FlashMint {␊
contract MyToken is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC1363, ERC20Permit, ERC20Votes, ERC20FlashMint {␊
bytes32 public constant TOKEN_BRIDGE_ROLE = keccak256("TOKEN_BRIDGE_ROLE");␊
error Unauthorized();␊
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
Expand Down Expand Up @@ -971,7 +989,7 @@ Generated by [AVA](https://avajs.dev).
function supportsInterface(bytes4 interfaceId)␊
public␊
view␊
override(ERC20Bridgeable, AccessControl)␊
override(ERC20Bridgeable, AccessControl, ERC1363)␊
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious how ordering is done. Not a blocker (I'd merge like that) but if we can clarify this in a further PR that would be great.

returns (bool)␊
{␊
return super.supportsInterface(interfaceId);␊
Expand All @@ -997,6 +1015,7 @@ Generated by [AVA](https://avajs.dev).
pragma solidity ^0.8.22;␊
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";␊
import {ERC1363Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC1363Upgradeable.sol";␊
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊
import {ERC20BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊
import {ERC20FlashMintUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊
Expand All @@ -1006,7 +1025,7 @@ Generated by [AVA](https://avajs.dev).
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊
import {NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol";␊
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable {␊
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC1363Upgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable {␊
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");␊
Expand All @@ -1022,6 +1041,7 @@ Generated by [AVA](https://avajs.dev).
__ERC20Burnable_init();␊
__ERC20Pausable_init();␊
__AccessControl_init();␊
__ERC1363_init();␊
__ERC20Permit_init("MyToken");␊
__ERC20Votes_init();␊
__ERC20FlashMint_init();␊
Expand Down Expand Up @@ -1052,6 +1072,15 @@ Generated by [AVA](https://avajs.dev).
{␊
super._update(from, to, value);␊
}␊
function supportsInterface(bytes4 interfaceId)␊
public␊
view␊
override(AccessControlUpgradeable, ERC1363Upgradeable)␊
returns (bool)␊
{␊
return super.supportsInterface(interfaceId);␊
}␊
function nonces(address owner)␊
public␊
Expand All @@ -1073,6 +1102,7 @@ Generated by [AVA](https://avajs.dev).
pragma solidity ^0.8.22;␊
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";␊
import {ERC1363Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC1363Upgradeable.sol";␊
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊
import {ERC20BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊
import {ERC20FlashMintUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊
Expand All @@ -1083,7 +1113,7 @@ Generated by [AVA](https://avajs.dev).
import {NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol";␊
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessControlUpgradeable, ERC1363Upgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");␊
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");␊
Expand All @@ -1100,6 +1130,7 @@ Generated by [AVA](https://avajs.dev).
__ERC20Burnable_init();␊
__ERC20Pausable_init();␊
__AccessControl_init();␊
__ERC1363_init();␊
__ERC20Permit_init("MyToken");␊
__ERC20Votes_init();␊
__ERC20FlashMint_init();␊
Expand Down Expand Up @@ -1138,6 +1169,15 @@ Generated by [AVA](https://avajs.dev).
{␊
super._update(from, to, value);␊
}␊
function supportsInterface(bytes4 interfaceId)␊
public␊
view␊
override(AccessControlUpgradeable, ERC1363Upgradeable)␊
returns (bool)␊
{␊
return super.supportsInterface(interfaceId);␊
}␊
function nonces(address owner)␊
public␊
Expand All @@ -1159,6 +1199,7 @@ Generated by [AVA](https://avajs.dev).
pragma solidity ^0.8.22;␊
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";␊
import {ERC1363Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC1363Upgradeable.sol";␊
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊
import {ERC20BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";␊
import {ERC20FlashMintUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20FlashMintUpgradeable.sol";␊
Expand All @@ -1169,7 +1210,7 @@ Generated by [AVA](https://avajs.dev).
import {NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol";␊
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessManagedUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
contract MyToken is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, AccessManagedUpgradeable, ERC1363Upgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊
/// @custom:oz-upgrades-unsafe-allow constructor␊
constructor() {␊
_disableInitializers();␊
Expand All @@ -1182,6 +1223,7 @@ Generated by [AVA](https://avajs.dev).
__ERC20Burnable_init();␊
__ERC20Pausable_init();␊
__AccessManaged_init(initialAuthority);␊
__ERC1363_init();␊
__ERC20Permit_init("MyToken");␊
__ERC20Votes_init();␊
__ERC20FlashMint_init();␊
Expand Down
Binary file modified packages/core/solidity/src/erc20.test.ts.snap
Binary file not shown.
16 changes: 16 additions & 0 deletions packages/core/solidity/src/erc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface ERC20Options extends CommonOptions {
premint?: string;
premintChainId?: string;
mintable?: boolean;
callback?: boolean;
permit?: boolean;
/**
* Whether to keep track of historical balances for voting in on-chain governance, and optionally specify the clock mode.
Expand All @@ -44,6 +45,7 @@ export const defaults: Required<ERC20Options> = {
premint: '0',
premintChainId: '',
mintable: false,
callback: false,
permit: true,
votes: false,
flashmint: false,
Expand All @@ -62,6 +64,7 @@ export function withDefaults(opts: ERC20Options): Required<ERC20Options> {
premint: opts.premint || defaults.premint,
premintChainId: opts.premintChainId || defaults.premintChainId,
mintable: opts.mintable ?? defaults.mintable,
callback: opts.callback ?? defaults.callback,
permit: opts.permit ?? defaults.permit,
votes: opts.votes ?? defaults.votes,
flashmint: opts.flashmint ?? defaults.flashmint,
Expand Down Expand Up @@ -106,6 +109,10 @@ export function buildERC20(opts: ERC20Options): ContractBuilder {
addMintable(c, access);
}

if (allOpts.callback) {
addCallback(c);
}

// Note: Votes requires Permit
if (allOpts.permit || allOpts.votes) {
addPermit(c, allOpts.name);
Expand Down Expand Up @@ -247,6 +254,15 @@ function addMintable(c: ContractBuilder, access: Access) {
c.addFunctionCode('_mint(to, amount);', functions.mint);
}

function addCallback(c: ContractBuilder) {
const ERC1363 = {
name: 'ERC1363',
path: '@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol',
};
c.addParent(ERC1363);
c.addOverride(ERC1363, supportsInterface);
}

function addPermit(c: ContractBuilder, name: string) {
const ERC20Permit = {
name: 'ERC20Permit',
Expand Down
1 change: 1 addition & 0 deletions packages/core/solidity/src/generate/erc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const blueprint = {
burnable: booleans,
pausable: booleans,
mintable: booleans,
callback: booleans,
permit: booleans,
votes: [...booleans, ...clockModeOptions] as const,
flashmint: booleans,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/solidity/src/generate/stablecoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const erc20Basic = {
burnable: [false] as const,
pausable: [false] as const,
mintable: [false] as const,
callback: [false] as const,
permit: [false] as const,
votes: [false] as const,
flashmint: [false] as const,
Expand All @@ -28,6 +29,7 @@ const erc20Full = {
burnable: [true] as const,
pausable: [true] as const,
mintable: [true] as const,
callback: [true] as const,
permit: [true] as const,
votes: ['timestamp'] as const,
flashmint: [true] as const,
Expand Down
6 changes: 6 additions & 0 deletions packages/core/solidity/src/stablecoin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ testStablecoin('stablecoin mintable with roles', {
access: 'roles',
});

testStablecoin('stablecoin callback', {
callback: true,
});

testStablecoin('stablecoin permit', {
permit: true,
});
Expand Down Expand Up @@ -118,6 +122,7 @@ testStablecoin('stablecoin full', {
burnable: true,
mintable: true,
pausable: true,
callback: true,
permit: true,
votes: true,
flashmint: true,
Expand All @@ -142,6 +147,7 @@ testAPIEquivalence('stablecoin API full', {
burnable: true,
mintable: true,
pausable: true,
callback: true,
permit: true,
votes: true,
flashmint: true,
Expand Down
22 changes: 20 additions & 2 deletions packages/core/solidity/src/stablecoin.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,23 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## stablecoin callback

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Contracts ^5.0.0␊
pragma solidity ^0.8.22;␊
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊
contract MyStablecoin is ERC20, ERC1363, ERC20Permit {␊
constructor() ERC20("MyStablecoin", "MST") ERC20Permit("MyStablecoin") {}␊
}␊
`

## stablecoin permit

> Snapshot 1
Expand Down Expand Up @@ -575,6 +592,7 @@ Generated by [AVA](https://avajs.dev).
pragma solidity ^0.8.22;␊
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";␊
import {ERC1363} from "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";␊
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
import {ERC20Allowlist} from "@openzeppelin/community-contracts/contracts/token/ERC20/extensions/ERC20Allowlist.sol";␊
import {ERC20Bridgeable} from "@openzeppelin/community-contracts/contracts/token/ERC20/extensions/ERC20Bridgeable.sol";␊
Expand All @@ -586,7 +604,7 @@ Generated by [AVA](https://avajs.dev).
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";␊
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";␊
contract MyStablecoin is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC20Permit, ERC20Votes, ERC20FlashMint, ERC20Custodian, ERC20Allowlist {␊
contract MyStablecoin is ERC20, ERC20Bridgeable, AccessControl, ERC20Burnable, ERC20Pausable, ERC1363, ERC20Permit, ERC20Votes, ERC20FlashMint, ERC20Custodian, ERC20Allowlist {␊
bytes32 public constant TOKEN_BRIDGE_ROLE = keccak256("TOKEN_BRIDGE_ROLE");␊
error Unauthorized();␊
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");␊
Expand Down Expand Up @@ -656,7 +674,7 @@ Generated by [AVA](https://avajs.dev).
function supportsInterface(bytes4 interfaceId)␊
public␊
view␊
override(ERC20Bridgeable, AccessControl)␊
override(ERC20Bridgeable, AccessControl, ERC1363)␊
returns (bool)␊
{␊
return super.supportsInterface(interfaceId);␊
Expand Down
Binary file modified packages/core/solidity/src/stablecoin.test.ts.snap
Binary file not shown.
8 changes: 8 additions & 0 deletions packages/ui/src/solidity/ERC20Controls.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@
</HelpTooltip>
</label>

<label class:checked={opts.callback}>
<input type="checkbox" bind:checked={opts.callback}>
Callback
<HelpTooltip link="https://docs.openzeppelin.com/contracts/5.x/api/token/erc20#ERC1363">
Supports code execution after transfers and approvals on recipient contracts in a single transaction.
</HelpTooltip>
</label>

<label class:checked={opts.permit || opts.votes}>
<input type="checkbox" bind:checked={opts.permit}>
Permit
Expand Down
8 changes: 8 additions & 0 deletions packages/ui/src/solidity/RealWorldAssetControls.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@
</HelpTooltip>
</label>

<label class:checked={opts.callback}>
<input type="checkbox" bind:checked={opts.callback}>
Callback
<HelpTooltip link="https://docs.openzeppelin.com/contracts/5.x/api/token/erc20#ERC1363">
Supports code execution after transfers and approvals on recipient contracts in a single transaction.
</HelpTooltip>
</label>

<label class:checked={opts.permit || opts.votes}>
<input type="checkbox" bind:checked={opts.permit}>
Permit
Expand Down
Loading
Loading