Skip to content

Commit d6f805d

Browse files
cairoethrenovate[bot]ericglau
authored
Add stablecoin and real world asset tabs (#404)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Eric Lau <[email protected]>
1 parent 057fcee commit d6f805d

19 files changed

+1543
-7
lines changed

packages/core/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 0.4.5 (2024-11-18)
4+
5+
- Add `stablecoin` and `realWorldAsset` contract types. ([#404](https://github.com/OpenZeppelin/contracts-wizard/pull/404))
6+
**Note:** `stablecoin` and `realWorldAsset` are experimental and may be subject to change.
7+
38
## 0.4.4 (2024-10-23)
49

510
### Potentially breaking changes

packages/core/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ The following contract types are supported:
1616
- `erc20`
1717
- `erc721`
1818
- `erc1155`
19+
- `stablecoin`
20+
- `realWorldAsset`
1921
- `governor`
2022
- `custom`
2123

24+
Note that `stablecoin` and `realWorldAsset` are experimental and may be subject to change.
25+
2226
Each contract type has functions/constants as defined below.
2327

2428
### Functions
@@ -34,6 +38,9 @@ function print(opts?: ERC721Options): string
3438
function print(opts?: ERC1155Options): string
3539
```
3640
```js
41+
function print(opts?: StablecoinOptions): string
42+
```
43+
```js
3744
function print(opts?: GovernorOptions): string
3845
```
3946
```js
@@ -52,6 +59,9 @@ const defaults: Required<ERC721Options>
5259
const defaults: Required<ERC1155Options>
5360
```
5461
```js
62+
const defaults: Required<StablecoinOptions>
63+
```
64+
```js
5565
const defaults: Required<GovernorOptions>
5666
```
5767
```js
@@ -70,6 +80,9 @@ function isAccessControlRequired(opts: Partial<ERC721Options>): boolean
7080
function isAccessControlRequired(opts: Partial<ERC1155Options>): boolean
7181
```
7282
```js
83+
function isAccessControlRequired(opts: Partial<StablecoinOptions>): boolean
84+
```
85+
```js
7386
function isAccessControlRequired(opts: Partial<GovernorOptions>): boolean
7487
```
7588
```js

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openzeppelin/wizard",
3-
"version": "0.4.4",
3+
"version": "0.4.5",
44
"description": "A boilerplate generator to get started with OpenZeppelin Contracts",
55
"license": "MIT",
66
"repository": "github:OpenZeppelin/contracts-wizard",
@@ -20,6 +20,7 @@
2020
"update-env": "rm ./src/environments/hardhat/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat && rm ./src/environments/hardhat/upgradeable/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat/upgradeable"
2121
},
2222
"devDependencies": {
23+
"@openzeppelin/community-contracts": "https://github.com/OpenZeppelin/openzeppelin-community-contracts",
2324
"@openzeppelin/contracts": "^5.1.0",
2425
"@openzeppelin/contracts-upgradeable": "^5.1.0",
2526
"@types/node": "^18.0.0",

packages/core/src/api.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { CommonOptions } from './common-options';
22
import { printERC20, defaults as erc20defaults, isAccessControlRequired as erc20IsAccessControlRequired, ERC20Options } from './erc20';
33
import { printERC721, defaults as erc721defaults, isAccessControlRequired as erc721IsAccessControlRequired, ERC721Options } from './erc721';
44
import { printERC1155, defaults as erc1155defaults, isAccessControlRequired as erc1155IsAccessControlRequired, ERC1155Options } from './erc1155';
5+
import { printStablecoin, defaults as stablecoinDefaults, isAccessControlRequired as stablecoinIsAccessControlRequired, StablecoinOptions } from './stablecoin';
56
import { printGovernor, defaults as governorDefaults, isAccessControlRequired as governorIsAccessControlRequired, GovernorOptions } from './governor';
67
import { printCustom, defaults as customDefaults, isAccessControlRequired as customIsAccessControlRequired, CustomOptions } from './custom';
78

@@ -26,6 +27,8 @@ export interface WizardContractAPI<Options extends CommonOptions> {
2627
export type ERC20 = WizardContractAPI<ERC20Options>;
2728
export type ERC721 = WizardContractAPI<ERC721Options>;
2829
export type ERC1155 = WizardContractAPI<ERC1155Options>;
30+
export type Stablecoin = WizardContractAPI<StablecoinOptions>;
31+
export type RealWorldAsset = WizardContractAPI<StablecoinOptions>;
2932
export type Governor = WizardContractAPI<GovernorOptions>;
3033
export type Custom = WizardContractAPI<CustomOptions>;
3134

@@ -44,6 +47,16 @@ export const erc1155: ERC1155 = {
4447
defaults: erc1155defaults,
4548
isAccessControlRequired: erc1155IsAccessControlRequired
4649
}
50+
export const stablecoin: Stablecoin = {
51+
print: printStablecoin,
52+
defaults: stablecoinDefaults,
53+
isAccessControlRequired: stablecoinIsAccessControlRequired
54+
}
55+
export const realWorldAsset: RealWorldAsset = {
56+
print: printStablecoin,
57+
defaults: stablecoinDefaults,
58+
isAccessControlRequired: stablecoinIsAccessControlRequired
59+
}
4760
export const governor: Governor = {
4861
print: printGovernor,
4962
defaults: governorDefaults,

packages/core/src/build-generic.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import { CustomOptions, buildCustom } from './custom';
22
import { ERC20Options, buildERC20 } from './erc20';
33
import { ERC721Options, buildERC721 } from './erc721';
44
import { ERC1155Options, buildERC1155 } from './erc1155';
5+
import { StablecoinOptions, buildStablecoin } from './stablecoin';
56
import { GovernorOptions, buildGovernor } from './governor';
67

78
export interface KindedOptions {
89
ERC20: { kind: 'ERC20' } & ERC20Options;
910
ERC721: { kind: 'ERC721' } & ERC721Options;
1011
ERC1155: { kind: 'ERC1155' } & ERC1155Options;
12+
Stablecoin: { kind: 'Stablecoin' } & StablecoinOptions;
13+
RealWorldAsset: { kind: 'RealWorldAsset' } & StablecoinOptions;
1114
Governor: { kind: 'Governor' } & GovernorOptions;
1215
Custom: { kind: 'Custom' } & CustomOptions;
1316
}
@@ -25,6 +28,12 @@ export function buildGeneric(opts: GenericOptions) {
2528
case 'ERC1155':
2629
return buildERC1155(opts);
2730

31+
case 'Stablecoin':
32+
return buildStablecoin(opts);
33+
34+
case 'RealWorldAsset':
35+
return buildStablecoin(opts);
36+
2837
case 'Governor':
2938
return buildGovernor(opts);
3039

packages/core/src/generate/sources.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import crypto from 'crypto';
55
import { generateERC20Options } from './erc20';
66
import { generateERC721Options } from './erc721';
77
import { generateERC1155Options } from './erc1155';
8+
import { generateStablecoinOptions } from './stablecoin';
89
import { generateGovernorOptions } from './governor';
910
import { generateCustomOptions } from './custom';
1011
import { buildGeneric, GenericOptions, KindedOptions } from '../build-generic';
@@ -36,6 +37,18 @@ export function* generateOptions(kind?: Kind): Generator<GenericOptions> {
3637
}
3738
}
3839

40+
if (!kind || kind === 'Stablecoin') {
41+
for (const kindOpts of generateStablecoinOptions()) {
42+
yield { kind: 'Stablecoin', ...kindOpts };
43+
}
44+
}
45+
46+
if (!kind || kind === 'RealWorldAsset') {
47+
for (const kindOpts of generateStablecoinOptions()) {
48+
yield { kind: 'RealWorldAsset', ...kindOpts };
49+
}
50+
}
51+
3952
if (!kind || kind === 'Governor') {
4053
for (const kindOpts of generateGovernorOptions()) {
4154
yield { kind: 'Governor', ...kindOpts };
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { StablecoinOptions } from '../stablecoin';
2+
import { accessOptions } from '../set-access-control';
3+
import { clockModeOptions } from '../set-clock-mode';
4+
import { infoOptions } from '../set-info';
5+
import { upgradeableOptions } from '../set-upgradeable';
6+
import { generateAlternatives } from './alternatives';
7+
8+
const booleans = [true, false];
9+
10+
const blueprint = {
11+
name: ['MyStablecoin'],
12+
symbol: ['MST'],
13+
burnable: booleans,
14+
pausable: booleans,
15+
mintable: booleans,
16+
permit: booleans,
17+
limitations: [false, 'allowlist', 'blocklist'] as const,
18+
votes: [ ...booleans, ...clockModeOptions ] as const,
19+
flashmint: booleans,
20+
premint: ['1'],
21+
custodian: booleans,
22+
access: accessOptions,
23+
upgradeable: upgradeableOptions,
24+
info: infoOptions,
25+
};
26+
27+
export function* generateStablecoinOptions(): Generator<Required<StablecoinOptions>> {
28+
for (const opts of generateAlternatives(blueprint)) {
29+
yield { ...opts, upgradeable: false };
30+
}
31+
}

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ export { OptionsError } from './error';
1919
export type { Kind } from './kind';
2020
export { sanitizeKind } from './kind';
2121

22-
export { erc20, erc721, erc1155, governor, custom } from './api';
22+
export { erc20, erc721, erc1155, stablecoin, realWorldAsset, governor, custom } from './api';
2323

2424
export { compatibleContractsSemver } from './utils/version';

packages/core/src/kind.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ function isKind<T>(value: Kind | T): value is Kind {
1717
case 'ERC20':
1818
case 'ERC1155':
1919
case 'ERC721':
20+
case 'Stablecoin':
21+
case 'RealWorldAsset':
2022
case 'Governor':
2123
case 'Custom':
2224
return true;

packages/core/src/stablecoin.test.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import test from 'ava';
2+
import { stablecoin } from '.';
3+
4+
import { buildStablecoin, StablecoinOptions } from './stablecoin';
5+
import { printContract } from './print';
6+
7+
function testStablecoin(title: string, opts: Partial<StablecoinOptions>) {
8+
test(title, t => {
9+
const c = buildStablecoin({
10+
name: 'MyStablecoin',
11+
symbol: 'MST',
12+
...opts,
13+
});
14+
t.snapshot(printContract(c));
15+
});
16+
}
17+
18+
/**
19+
* Tests external API for equivalence with internal API
20+
*/
21+
function testAPIEquivalence(title: string, opts?: StablecoinOptions) {
22+
test(title, t => {
23+
t.is(stablecoin.print(opts), printContract(buildStablecoin({
24+
name: 'MyStablecoin',
25+
symbol: 'MST',
26+
...opts,
27+
})));
28+
});
29+
}
30+
31+
testStablecoin('basic stablecoin', {});
32+
33+
testStablecoin('stablecoin burnable', {
34+
burnable: true,
35+
});
36+
37+
testStablecoin('stablecoin pausable', {
38+
pausable: true,
39+
access: 'ownable',
40+
});
41+
42+
testStablecoin('stablecoin pausable with roles', {
43+
pausable: true,
44+
access: 'roles',
45+
});
46+
47+
testStablecoin('stablecoin pausable with managed', {
48+
pausable: true,
49+
access: 'managed',
50+
});
51+
52+
testStablecoin('stablecoin burnable pausable', {
53+
burnable: true,
54+
pausable: true,
55+
});
56+
57+
testStablecoin('stablecoin preminted', {
58+
premint: '1000',
59+
});
60+
61+
testStablecoin('stablecoin premint of 0', {
62+
premint: '0',
63+
});
64+
65+
testStablecoin('stablecoin mintable', {
66+
mintable: true,
67+
access: 'ownable',
68+
});
69+
70+
testStablecoin('stablecoin mintable with roles', {
71+
mintable: true,
72+
access: 'roles',
73+
});
74+
75+
testStablecoin('stablecoin permit', {
76+
permit: true,
77+
});
78+
79+
testStablecoin('stablecoin custodian', {
80+
custodian: true,
81+
});
82+
83+
testStablecoin('stablecoin allowlist', {
84+
limitations: 'allowlist',
85+
});
86+
87+
testStablecoin('stablecoin blocklist', {
88+
limitations: 'blocklist',
89+
});
90+
91+
testStablecoin('stablecoin votes', {
92+
votes: true,
93+
});
94+
95+
testStablecoin('stablecoin votes + blocknumber', {
96+
votes: 'blocknumber',
97+
});
98+
99+
testStablecoin('stablecoin votes + timestamp', {
100+
votes: 'timestamp',
101+
});
102+
103+
testStablecoin('stablecoin flashmint', {
104+
flashmint: true,
105+
});
106+
107+
testAPIEquivalence('stablecoin API default');
108+
109+
testAPIEquivalence('stablecoin API basic', { name: 'CustomStablecoin', symbol: 'CST' });
110+
111+
testAPIEquivalence('stablecoin API full', {
112+
name: 'CustomStablecoin',
113+
symbol: 'CST',
114+
premint: '2000',
115+
access: 'roles',
116+
burnable: true,
117+
mintable: true,
118+
pausable: true,
119+
permit: true,
120+
votes: true,
121+
flashmint: true,
122+
limitations: 'allowlist',
123+
custodian: true
124+
});
125+
126+
test('stablecoin API assert defaults', async t => {
127+
t.is(stablecoin.print(stablecoin.defaults), stablecoin.print());
128+
});
129+
130+
test('stablecoin API isAccessControlRequired', async t => {
131+
t.is(stablecoin.isAccessControlRequired({ mintable: true }), true);
132+
t.is(stablecoin.isAccessControlRequired({ pausable: true }), true);
133+
t.is(stablecoin.isAccessControlRequired({ limitations: 'allowlist' }), true);
134+
t.is(stablecoin.isAccessControlRequired({ limitations: 'blocklist' }), true);
135+
});

0 commit comments

Comments
 (0)