@openzeppelin/wizard-stellar 0.6.4 → 0.7.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.d.ts.map +1 -1
- package/dist/account.js +18 -4
- package/dist/account.js.map +1 -1
- package/dist/add-votes.d.ts +3 -0
- package/dist/add-votes.d.ts.map +1 -0
- package/dist/add-votes.js +53 -0
- package/dist/add-votes.js.map +1 -0
- package/dist/fungible.d.ts +13 -2
- package/dist/fungible.d.ts.map +1 -1
- package/dist/fungible.js +147 -77
- package/dist/fungible.js.map +1 -1
- package/dist/generate/fungible.d.ts.map +1 -1
- package/dist/generate/fungible.js +2 -0
- package/dist/generate/fungible.js.map +1 -1
- package/dist/generate/non-fungible.d.ts.map +1 -1
- package/dist/generate/non-fungible.js +2 -0
- package/dist/generate/non-fungible.js.map +1 -1
- package/dist/generate/stablecoin.d.ts.map +1 -1
- package/dist/generate/stablecoin.js +2 -0
- package/dist/generate/stablecoin.js.map +1 -1
- package/dist/governor.d.ts.map +1 -1
- package/dist/governor.js +9 -1
- package/dist/governor.js.map +1 -1
- package/dist/non-fungible.d.ts +2 -0
- package/dist/non-fungible.d.ts.map +1 -1
- package/dist/non-fungible.js +152 -126
- package/dist/non-fungible.js.map +1 -1
- package/dist/print.js +16 -1
- package/dist/print.js.map +1 -1
- package/dist/stablecoin.d.ts.map +1 -1
- package/dist/stablecoin.js +6 -31
- package/dist/stablecoin.js.map +1 -1
- package/dist/utils/compose.d.ts +6 -0
- package/dist/utils/compose.d.ts.map +1 -0
- package/dist/utils/compose.js +12 -0
- package/dist/utils/compose.js.map +1 -0
- package/dist/utils/version.d.ts +4 -4
- package/dist/utils/version.js +3 -3
- package/dist/vault.d.ts +1 -1
- package/dist/vault.d.ts.map +1 -1
- package/dist/vault.js +27 -7
- package/dist/vault.js.map +1 -1
- package/dist/zip-shared.d.ts.map +1 -1
- package/dist/zip-shared.js +4 -3
- package/dist/zip-shared.js.map +1 -1
- package/package.json +1 -1
- package/src/account.ts +18 -4
- package/src/add-votes.ts +53 -0
- package/src/fungible.ts +171 -86
- package/src/generate/fungible.ts +2 -0
- package/src/generate/non-fungible.ts +2 -0
- package/src/generate/stablecoin.ts +2 -0
- package/src/governor.ts +10 -1
- package/src/non-fungible.ts +182 -143
- package/src/print.ts +14 -1
- package/src/stablecoin.ts +9 -37
- package/src/utils/compose.ts +8 -0
- package/src/utils/version.ts +3 -3
- package/src/vault.ts +32 -7
- package/src/zip-shared.ts +4 -3
package/src/fungible.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Access } from './set-access-control';
|
|
|
3
3
|
import { requireAccessControl, setAccessControl } from './set-access-control';
|
|
4
4
|
import { addPausable } from './add-pausable';
|
|
5
5
|
import { addUpgradeable } from './add-upgradeable';
|
|
6
|
+
import { addVotes } from './add-votes';
|
|
6
7
|
import { defineFunctions } from './utils/define-functions';
|
|
7
8
|
import type { CommonContractOptions } from './common-options';
|
|
8
9
|
import { withCommonContractDefaults, getSelfArg } from './common-options';
|
|
@@ -11,9 +12,11 @@ import { OptionsError } from './error';
|
|
|
11
12
|
import { contractDefaults as commonDefaults } from './common-options';
|
|
12
13
|
import { printContract } from './print';
|
|
13
14
|
import { toByteArray, toUint } from './utils/convert-strings';
|
|
15
|
+
import { printComposedContractType } from './utils/compose';
|
|
14
16
|
import { pickKeys } from '@openzeppelin/wizard-common';
|
|
15
17
|
|
|
16
18
|
const DEFAULT_DECIMALS = 7;
|
|
19
|
+
const I128_MAX = (1n << 127n) - 1n;
|
|
17
20
|
|
|
18
21
|
export const defaults: Required<FungibleOptions> = {
|
|
19
22
|
name: 'MyToken',
|
|
@@ -21,6 +24,8 @@ export const defaults: Required<FungibleOptions> = {
|
|
|
21
24
|
decimals: DEFAULT_DECIMALS.toString(),
|
|
22
25
|
burnable: false,
|
|
23
26
|
votes: false,
|
|
27
|
+
totalSupply: false,
|
|
28
|
+
cap: '',
|
|
24
29
|
pausable: false,
|
|
25
30
|
upgradeable: false,
|
|
26
31
|
premint: '0',
|
|
@@ -40,12 +45,19 @@ export interface FungibleOptions extends CommonContractOptions {
|
|
|
40
45
|
decimals?: string;
|
|
41
46
|
burnable?: boolean;
|
|
42
47
|
votes?: boolean;
|
|
48
|
+
totalSupply?: boolean;
|
|
49
|
+
cap?: string;
|
|
43
50
|
pausable?: boolean;
|
|
44
51
|
upgradeable?: boolean;
|
|
45
52
|
premint?: string;
|
|
46
53
|
mintable?: boolean;
|
|
47
54
|
}
|
|
48
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Contract type restricting which accounts can use the token, selected by the Stablecoin limitations.
|
|
58
|
+
*/
|
|
59
|
+
export type TransferPolicy = 'AllowList' | 'BlockList';
|
|
60
|
+
|
|
49
61
|
export function withDefaults(opts: FungibleOptions): Required<FungibleOptions> {
|
|
50
62
|
return {
|
|
51
63
|
...opts,
|
|
@@ -53,6 +65,8 @@ export function withDefaults(opts: FungibleOptions): Required<FungibleOptions> {
|
|
|
53
65
|
decimals: opts.decimals ?? defaults.decimals,
|
|
54
66
|
burnable: opts.burnable ?? defaults.burnable,
|
|
55
67
|
votes: opts.votes ?? defaults.votes,
|
|
68
|
+
totalSupply: opts.totalSupply ?? defaults.totalSupply,
|
|
69
|
+
cap: opts.cap ?? defaults.cap,
|
|
56
70
|
pausable: opts.pausable ?? defaults.pausable,
|
|
57
71
|
upgradeable: opts.upgradeable ?? defaults.upgradeable,
|
|
58
72
|
premint: opts.premint || defaults.premint,
|
|
@@ -65,24 +79,71 @@ export function isAccessControlRequired(opts: Partial<FungibleOptions>): boolean
|
|
|
65
79
|
}
|
|
66
80
|
|
|
67
81
|
export function buildFungible(opts: FungibleOptions): ContractBuilder {
|
|
82
|
+
return buildFungibleWithTransferPolicy(opts);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Builds a Fungible contract whose contract type also includes the given transfer policy.
|
|
87
|
+
*/
|
|
88
|
+
export function buildFungibleWithTransferPolicy(
|
|
89
|
+
opts: FungibleOptions,
|
|
90
|
+
transferPolicy?: TransferPolicy,
|
|
91
|
+
): ContractBuilder {
|
|
68
92
|
const c = new ContractBuilder(opts.name);
|
|
69
93
|
|
|
70
94
|
const allOpts = withDefaults(opts);
|
|
71
95
|
|
|
72
96
|
const decimals = toUint(allOpts.decimals, 'decimals', 'u32');
|
|
73
97
|
|
|
98
|
+
const capped = allOpts.cap !== '';
|
|
99
|
+
// The cap is checked against the total supply, so it requires the supply to be tracked.
|
|
100
|
+
const totalSupply = allOpts.totalSupply || capped;
|
|
101
|
+
|
|
102
|
+
if (allOpts.votes && capped) {
|
|
103
|
+
throw new OptionsError({
|
|
104
|
+
votes: 'Votes extension cannot be used with a cap',
|
|
105
|
+
cap: 'Cap cannot be used with Votes extension',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (allOpts.votes && totalSupply) {
|
|
110
|
+
throw new OptionsError({
|
|
111
|
+
votes: 'Votes extension cannot be used with Total Supply extension',
|
|
112
|
+
totalSupply: 'Total Supply extension cannot be used with Votes extension',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Order follows the library's documentation, e.g. `Compose<(AllowList, Capped, TotalSupply)>`.
|
|
117
|
+
const contractTypes = [
|
|
118
|
+
...(transferPolicy ? [transferPolicy] : []),
|
|
119
|
+
...(allOpts.votes ? ['FungibleVotes'] : []),
|
|
120
|
+
...(capped ? ['Capped'] : []),
|
|
121
|
+
...(totalSupply ? ['TotalSupply'] : []),
|
|
122
|
+
];
|
|
123
|
+
|
|
74
124
|
addBase(
|
|
75
125
|
c,
|
|
76
126
|
toByteArray(allOpts.name),
|
|
77
127
|
toByteArray(allOpts.symbol),
|
|
78
128
|
decimals,
|
|
79
|
-
|
|
129
|
+
contractTypes,
|
|
80
130
|
allOpts.pausable,
|
|
81
131
|
allOpts.explicitImplementations,
|
|
82
132
|
);
|
|
83
133
|
|
|
134
|
+
if (allOpts.votes) {
|
|
135
|
+
addVotes(c, allOpts.explicitImplementations);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (totalSupply) {
|
|
139
|
+
addTotalSupply(c, allOpts.explicitImplementations);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// The cap has to be set before the premint, which is checked against it.
|
|
143
|
+
const capAbsolute = capped ? addCap(c, allOpts.cap, decimals, allOpts.explicitImplementations) : undefined;
|
|
144
|
+
|
|
84
145
|
if (allOpts.premint) {
|
|
85
|
-
addPremint(c, allOpts.premint, decimals,
|
|
146
|
+
addPremint(c, allOpts.premint, decimals, capAbsolute);
|
|
86
147
|
}
|
|
87
148
|
|
|
88
149
|
if (allOpts.pausable) {
|
|
@@ -94,11 +155,11 @@ export function buildFungible(opts: FungibleOptions): ContractBuilder {
|
|
|
94
155
|
}
|
|
95
156
|
|
|
96
157
|
if (allOpts.burnable) {
|
|
97
|
-
addBurnable(c, allOpts.
|
|
158
|
+
addBurnable(c, allOpts.pausable, allOpts.explicitImplementations);
|
|
98
159
|
}
|
|
99
160
|
|
|
100
161
|
if (allOpts.mintable) {
|
|
101
|
-
addMintable(c, allOpts.access, allOpts.
|
|
162
|
+
addMintable(c, allOpts.access, allOpts.pausable, allOpts.explicitImplementations);
|
|
102
163
|
}
|
|
103
164
|
|
|
104
165
|
setAccessControl(c, allOpts.access, allOpts.explicitImplementations);
|
|
@@ -112,7 +173,7 @@ function addBase(
|
|
|
112
173
|
name: string,
|
|
113
174
|
symbol: string,
|
|
114
175
|
decimals: bigint,
|
|
115
|
-
|
|
176
|
+
contractTypes: string[],
|
|
116
177
|
pausable: boolean,
|
|
117
178
|
explicitImplementations: boolean,
|
|
118
179
|
) {
|
|
@@ -123,10 +184,9 @@ function addBase(
|
|
|
123
184
|
|
|
124
185
|
// Set token functions
|
|
125
186
|
c.addUseClause('stellar_tokens::fungible', 'Base');
|
|
187
|
+
c.addUseClause('stellar_tokens::fungible', 'Compose');
|
|
126
188
|
c.addUseClause('stellar_tokens::fungible', 'FungibleToken');
|
|
127
|
-
if (
|
|
128
|
-
c.addUseClause('stellar_tokens::fungible', 'ContractOverrides');
|
|
129
|
-
c.addUseClause('stellar_governance::votes', 'Votes');
|
|
189
|
+
if (contractTypes.includes('FungibleVotes')) {
|
|
130
190
|
c.addUseClause('stellar_tokens::fungible', 'votes::FungibleVotes');
|
|
131
191
|
}
|
|
132
192
|
c.addUseClause('soroban_sdk', 'contract');
|
|
@@ -142,7 +202,7 @@ function addBase(
|
|
|
142
202
|
traitName: 'FungibleToken',
|
|
143
203
|
structName: c.name,
|
|
144
204
|
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
145
|
-
assocType:
|
|
205
|
+
assocType: printComposedContractType(contractTypes.length > 0 ? contractTypes : ['Base']),
|
|
146
206
|
};
|
|
147
207
|
|
|
148
208
|
c.addTraitImplBlock(fungibleTokenTrait);
|
|
@@ -159,46 +219,82 @@ function addBase(
|
|
|
159
219
|
c.addFunctionTag(functions.transfer_from, 'when_not_paused', fungibleTokenTrait);
|
|
160
220
|
}
|
|
161
221
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
});
|
|
222
|
+
// `Base` implements every `FungibleToken` method inherently, while the other contract types provide
|
|
223
|
+
// some of them only through the `ContractOverrides` trait, which then has to be in scope. The
|
|
224
|
+
// supply-tracking contract types provide transfers through it as well.
|
|
225
|
+
const tracksSupply = contractTypes.includes('TotalSupply');
|
|
226
|
+
if ((explicitImplementations && contractTypes.length > 0) || (pausable && tracksSupply)) {
|
|
227
|
+
c.addUseClause('stellar_tokens::fungible', 'ContractOverrides');
|
|
169
228
|
}
|
|
170
229
|
}
|
|
171
230
|
|
|
172
|
-
function
|
|
173
|
-
c
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
|
|
231
|
+
function addTotalSupply(c: ContractBuilder, explicitImplementations: boolean) {
|
|
232
|
+
c.addUseClause('stellar_tokens::fungible', 'total_supply::{FungibleTotalSupply, TotalSupply}');
|
|
233
|
+
|
|
234
|
+
const fungibleTotalSupplyTrait = {
|
|
235
|
+
traitName: 'FungibleTotalSupply',
|
|
236
|
+
structName: c.name,
|
|
237
|
+
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
238
|
+
section: 'Extensions',
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
if (explicitImplementations) c.addTraitFunction(fungibleTotalSupplyTrait, functions.total_supply);
|
|
242
|
+
else c.addTraitImplBlock(fungibleTotalSupplyTrait);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function addCap(c: ContractBuilder, cap: string, decimals: bigint, explicitImplementations: boolean): bigint {
|
|
246
|
+
if (!premintPattern.test(cap)) {
|
|
247
|
+
throw new OptionsError({
|
|
248
|
+
cap: 'Not a valid number',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const capAbsolute = toUint(getInitialSupply(cap, Number(decimals), 'cap'), 'cap', 'u128');
|
|
253
|
+
// `Capped::set_cap` takes an `i128`.
|
|
254
|
+
validateI128(capAbsolute, 'cap');
|
|
255
|
+
if (capAbsolute === 0n) {
|
|
256
|
+
throw new OptionsError({
|
|
257
|
+
cap: 'Cap must be greater than 0',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
c.addUseClause('stellar_tokens::fungible', 'capped::{Capped, FungibleCapped}');
|
|
262
|
+
c.addConstructorCode(`Capped::set_cap(e, ${capAbsolute});`);
|
|
181
263
|
|
|
264
|
+
const fungibleCappedTrait = {
|
|
265
|
+
traitName: 'FungibleCapped',
|
|
266
|
+
structName: c.name,
|
|
267
|
+
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
268
|
+
section: 'Extensions',
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
if (explicitImplementations) c.addTraitFunction(fungibleCappedTrait, functions.cap);
|
|
272
|
+
else c.addTraitImplBlock(fungibleCappedTrait);
|
|
273
|
+
|
|
274
|
+
return capAbsolute;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function addMintable(c: ContractBuilder, access: Access, pausable: boolean, explicitImplementations: boolean) {
|
|
182
278
|
switch (access) {
|
|
183
279
|
case false:
|
|
184
280
|
break;
|
|
185
281
|
case 'ownable': {
|
|
186
|
-
c.addFreeFunction(
|
|
282
|
+
c.addFreeFunction(functions.mint);
|
|
187
283
|
|
|
188
|
-
requireAccessControl(c, undefined,
|
|
284
|
+
requireAccessControl(c, undefined, functions.mint, access, undefined, explicitImplementations);
|
|
189
285
|
|
|
190
286
|
if (pausable) {
|
|
191
|
-
c.addFunctionTag(
|
|
287
|
+
c.addFunctionTag(functions.mint, 'when_not_paused');
|
|
192
288
|
}
|
|
193
289
|
break;
|
|
194
290
|
}
|
|
195
291
|
case 'roles': {
|
|
196
|
-
c.addFreeFunction(
|
|
292
|
+
c.addFreeFunction(functions.mint_with_caller);
|
|
197
293
|
|
|
198
294
|
requireAccessControl(
|
|
199
295
|
c,
|
|
200
296
|
undefined,
|
|
201
|
-
|
|
297
|
+
functions.mint_with_caller,
|
|
202
298
|
access,
|
|
203
299
|
{
|
|
204
300
|
useMacro: true,
|
|
@@ -209,7 +305,7 @@ function addMintable(
|
|
|
209
305
|
);
|
|
210
306
|
|
|
211
307
|
if (pausable) {
|
|
212
|
-
c.addFunctionTag(
|
|
308
|
+
c.addFunctionTag(functions.mint_with_caller, 'when_not_paused');
|
|
213
309
|
}
|
|
214
310
|
break;
|
|
215
311
|
}
|
|
@@ -220,7 +316,7 @@ function addMintable(
|
|
|
220
316
|
}
|
|
221
317
|
}
|
|
222
318
|
|
|
223
|
-
function addBurnable(c: ContractBuilder,
|
|
319
|
+
function addBurnable(c: ContractBuilder, pausable: boolean, explicitImplementations: boolean) {
|
|
224
320
|
c.addUseClause('stellar_tokens::fungible', 'burnable::FungibleBurnable');
|
|
225
321
|
|
|
226
322
|
const fungibleBurnableTrait = {
|
|
@@ -230,29 +326,34 @@ function addBurnable(c: ContractBuilder, votes: boolean, pausable: boolean, expl
|
|
|
230
326
|
section: 'Extensions',
|
|
231
327
|
};
|
|
232
328
|
|
|
233
|
-
|
|
234
|
-
const burnFromFn = votes ? votesFunctions.burn_from : functions.burn_from;
|
|
235
|
-
|
|
236
|
-
if (pausable || votes || explicitImplementations) {
|
|
329
|
+
if (pausable || explicitImplementations) {
|
|
237
330
|
if (pausable) {
|
|
238
331
|
c.addUseClause('stellar_macros', 'when_not_paused');
|
|
239
332
|
}
|
|
240
333
|
|
|
241
|
-
c.addTraitFunction(fungibleBurnableTrait,
|
|
242
|
-
c.addTraitFunction(fungibleBurnableTrait,
|
|
334
|
+
c.addTraitFunction(fungibleBurnableTrait, functions.burn);
|
|
335
|
+
c.addTraitFunction(fungibleBurnableTrait, functions.burn_from);
|
|
243
336
|
|
|
244
337
|
if (pausable) {
|
|
245
|
-
c.addFunctionTag(
|
|
246
|
-
c.addFunctionTag(
|
|
338
|
+
c.addFunctionTag(functions.burn, 'when_not_paused', fungibleBurnableTrait);
|
|
339
|
+
c.addFunctionTag(functions.burn_from, 'when_not_paused', fungibleBurnableTrait);
|
|
247
340
|
}
|
|
248
341
|
} else {
|
|
249
342
|
c.addTraitImplBlock(fungibleBurnableTrait);
|
|
250
343
|
}
|
|
251
344
|
}
|
|
252
345
|
|
|
253
|
-
|
|
346
|
+
function validateI128(value: bigint, field: string) {
|
|
347
|
+
if (value > I128_MAX) {
|
|
348
|
+
throw new OptionsError({
|
|
349
|
+
[field]: 'Value is greater than i128 max value',
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export const premintPattern = /^\d*(?:\.\d*)?$/;
|
|
254
355
|
|
|
255
|
-
function addPremint(c: ContractBuilder, amount: string, decimals: bigint,
|
|
356
|
+
function addPremint(c: ContractBuilder, amount: string, decimals: bigint, capAbsolute?: bigint) {
|
|
256
357
|
if (amount !== undefined && amount !== '0') {
|
|
257
358
|
if (!premintPattern.test(amount)) {
|
|
258
359
|
throw new OptionsError({
|
|
@@ -260,11 +361,19 @@ function addPremint(c: ContractBuilder, amount: string, decimals: bigint, votes:
|
|
|
260
361
|
});
|
|
261
362
|
}
|
|
262
363
|
|
|
263
|
-
// TODO: handle signed int?
|
|
264
364
|
const premintAbsolute = toUint(getInitialSupply(amount, Number(decimals)), 'premint', 'u128');
|
|
365
|
+
// `mint` takes an `i128`.
|
|
366
|
+
validateI128(premintAbsolute, 'premint');
|
|
367
|
+
|
|
368
|
+
if (capAbsolute !== undefined && premintAbsolute > capAbsolute) {
|
|
369
|
+
throw new OptionsError({
|
|
370
|
+
premint: 'Premint exceeds the cap',
|
|
371
|
+
cap: 'Cap is lower than the premint',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
265
374
|
|
|
266
375
|
c.addConstructorArgument({ name: 'recipient', type: 'Address' });
|
|
267
|
-
c.addConstructorCode(
|
|
376
|
+
c.addConstructorCode(`<Self as FungibleToken>::ContractType::mint(e, &recipient, ${premintAbsolute});`);
|
|
268
377
|
}
|
|
269
378
|
}
|
|
270
379
|
|
|
@@ -273,15 +382,16 @@ function addPremint(c: ContractBuilder, amount: string, decimals: bigint, votes:
|
|
|
273
382
|
*
|
|
274
383
|
* @param premint Premint amount in token units, may be fractional
|
|
275
384
|
* @param decimals The number of decimals in the token
|
|
385
|
+
* @param field The option reported in errors
|
|
276
386
|
* @returns `premint` with zeros padded or removed based on `decimals`.
|
|
277
387
|
* @throws OptionsError if `premint` has more than one decimal character or is more precise than allowed by the `decimals` argument.
|
|
278
388
|
*/
|
|
279
|
-
export function getInitialSupply(premint: string, decimals: number): string {
|
|
389
|
+
export function getInitialSupply(premint: string, decimals: number, field = 'premint'): string {
|
|
280
390
|
let result;
|
|
281
391
|
const premintSegments = premint.split('.');
|
|
282
392
|
if (premintSegments.length > 2) {
|
|
283
393
|
throw new OptionsError({
|
|
284
|
-
|
|
394
|
+
[field]: 'Not a valid number',
|
|
285
395
|
});
|
|
286
396
|
} else {
|
|
287
397
|
const firstSegment = premintSegments[0] ?? '';
|
|
@@ -292,12 +402,12 @@ export function getInitialSupply(premint: string, decimals: number): string {
|
|
|
292
402
|
} catch {
|
|
293
403
|
// .repeat gives an error if decimals number is too large
|
|
294
404
|
throw new OptionsError({
|
|
295
|
-
|
|
405
|
+
[field]: 'Decimals number too large',
|
|
296
406
|
});
|
|
297
407
|
}
|
|
298
408
|
} else if (decimals < lastSegment.length) {
|
|
299
409
|
throw new OptionsError({
|
|
300
|
-
|
|
410
|
+
[field]: 'Too many decimals',
|
|
301
411
|
});
|
|
302
412
|
}
|
|
303
413
|
// concat segments without leading zeros
|
|
@@ -311,11 +421,6 @@ export function getInitialSupply(premint: string, decimals: number): string {
|
|
|
311
421
|
|
|
312
422
|
export const functions = defineFunctions({
|
|
313
423
|
// Token Functions
|
|
314
|
-
total_supply: {
|
|
315
|
-
args: [getSelfArg()],
|
|
316
|
-
returns: 'i128',
|
|
317
|
-
code: ['Self::ContractType::total_supply(e)'],
|
|
318
|
-
},
|
|
319
424
|
balance: {
|
|
320
425
|
args: [getSelfArg(), { name: 'account', type: 'Address' }],
|
|
321
426
|
returns: 'i128',
|
|
@@ -372,39 +477,19 @@ export const functions = defineFunctions({
|
|
|
372
477
|
},
|
|
373
478
|
|
|
374
479
|
// Extensions
|
|
375
|
-
|
|
376
|
-
args: [getSelfArg()
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
burn_from: {
|
|
380
|
-
args: [
|
|
381
|
-
getSelfArg(),
|
|
382
|
-
{ name: 'spender', type: 'Address' },
|
|
383
|
-
{ name: 'from', type: 'Address' },
|
|
384
|
-
{ name: 'amount', type: 'i128' },
|
|
385
|
-
],
|
|
386
|
-
code: ['Base::burn_from(e, &spender, &from, amount)'],
|
|
387
|
-
},
|
|
388
|
-
mint: {
|
|
389
|
-
args: [getSelfArg(), { name: 'account', type: 'Address' }, { name: 'amount', type: 'i128' }],
|
|
390
|
-
code: ['Base::mint(e, &account, amount);'],
|
|
480
|
+
total_supply: {
|
|
481
|
+
args: [getSelfArg()],
|
|
482
|
+
returns: 'i128',
|
|
483
|
+
code: ['Self::ContractType::total_supply(e)'],
|
|
391
484
|
},
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
{ name: 'account', type: 'Address' },
|
|
397
|
-
{ name: 'amount', type: 'i128' },
|
|
398
|
-
{ name: 'caller', type: 'Address' },
|
|
399
|
-
],
|
|
400
|
-
code: ['Base::mint(e, &account, amount);'],
|
|
485
|
+
cap: {
|
|
486
|
+
args: [getSelfArg()],
|
|
487
|
+
returns: 'i128',
|
|
488
|
+
code: ['Capped::cap(e)'],
|
|
401
489
|
},
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
const votesFunctions = defineFunctions({
|
|
405
490
|
burn: {
|
|
406
491
|
args: [getSelfArg(), { name: 'from', type: 'Address' }, { name: 'amount', type: 'i128' }],
|
|
407
|
-
code: ['
|
|
492
|
+
code: ['Self::ContractType::burn(e, &from, amount)'],
|
|
408
493
|
},
|
|
409
494
|
burn_from: {
|
|
410
495
|
args: [
|
|
@@ -413,11 +498,12 @@ const votesFunctions = defineFunctions({
|
|
|
413
498
|
{ name: 'from', type: 'Address' },
|
|
414
499
|
{ name: 'amount', type: 'i128' },
|
|
415
500
|
],
|
|
416
|
-
code: ['
|
|
501
|
+
code: ['Self::ContractType::burn_from(e, &spender, &from, amount)'],
|
|
417
502
|
},
|
|
503
|
+
// Minting goes through the contract type, which keeps the total supply, the cap and the voting units up to date.
|
|
418
504
|
mint: {
|
|
419
505
|
args: [getSelfArg(), { name: 'account', type: 'Address' }, { name: 'amount', type: 'i128' }],
|
|
420
|
-
code: ['
|
|
506
|
+
code: ['<Self as FungibleToken>::ContractType::mint(e, &account, amount);'],
|
|
421
507
|
},
|
|
422
508
|
mint_with_caller: {
|
|
423
509
|
name: 'mint',
|
|
@@ -427,12 +513,11 @@ const votesFunctions = defineFunctions({
|
|
|
427
513
|
{ name: 'amount', type: 'i128' },
|
|
428
514
|
{ name: 'caller', type: 'Address' },
|
|
429
515
|
],
|
|
430
|
-
code: ['
|
|
516
|
+
code: ['<Self as FungibleToken>::ContractType::mint(e, &account, amount);'],
|
|
431
517
|
},
|
|
432
518
|
});
|
|
433
519
|
|
|
434
520
|
const fungibleTokenTraitFunctions = pickKeys(functions, [
|
|
435
|
-
'total_supply',
|
|
436
521
|
'balance',
|
|
437
522
|
'allowance',
|
|
438
523
|
'transfer',
|
package/src/generate/fungible.ts
CHANGED
package/src/governor.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommonContractOptions } from './common-options';
|
|
2
2
|
import { contractDefaults as commonDefaults, getSelfArg, withCommonContractDefaults } from './common-options';
|
|
3
3
|
import { ContractBuilder } from './contract';
|
|
4
|
+
import { OptionsError } from './error';
|
|
4
5
|
import { printContract } from './print';
|
|
5
6
|
import { setAccessControl } from './set-access-control';
|
|
6
7
|
import { addUpgradeable } from './add-upgradeable';
|
|
@@ -59,12 +60,20 @@ export function buildGovernor(opts: GovernorOptions): ContractBuilder {
|
|
|
59
60
|
const allOpts = withDefaults(opts);
|
|
60
61
|
const c = new ContractBuilder(allOpts.name);
|
|
61
62
|
|
|
63
|
+
// The library rejects a zero voting period, which would make the constructor panic on deploy.
|
|
64
|
+
const votingPeriod = toUint(allOpts.votingPeriod, 'votingPeriod', 'u32');
|
|
65
|
+
if (votingPeriod === 0n) {
|
|
66
|
+
throw new OptionsError({
|
|
67
|
+
votingPeriod: 'Voting period must be greater than 0',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
62
71
|
addBase(
|
|
63
72
|
c,
|
|
64
73
|
toByteArray(allOpts.name),
|
|
65
74
|
toByteArray(allOpts.version),
|
|
66
75
|
toUint(allOpts.votingDelay, 'votingDelay', 'u32'),
|
|
67
|
-
|
|
76
|
+
votingPeriod,
|
|
68
77
|
toUint(allOpts.proposalThreshold, 'proposalThreshold', 'u128'),
|
|
69
78
|
toUint(allOpts.quorum, 'quorum', 'u128'),
|
|
70
79
|
allOpts.timelock,
|