@openzeppelin/wizard-stellar 0.4.4 → 0.5.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.
Files changed (64) hide show
  1. package/dist/add-pausable.d.ts +1 -1
  2. package/dist/add-pausable.d.ts.map +1 -1
  3. package/dist/add-pausable.js +8 -8
  4. package/dist/add-pausable.js.map +1 -1
  5. package/dist/add-upgradeable.d.ts +1 -1
  6. package/dist/add-upgradeable.d.ts.map +1 -1
  7. package/dist/add-upgradeable.js +2 -2
  8. package/dist/add-upgradeable.js.map +1 -1
  9. package/dist/common-options.d.ts +1 -0
  10. package/dist/common-options.d.ts.map +1 -1
  11. package/dist/common-options.js +2 -0
  12. package/dist/common-options.js.map +1 -1
  13. package/dist/contract.d.ts +2 -0
  14. package/dist/contract.d.ts.map +1 -1
  15. package/dist/contract.js +7 -0
  16. package/dist/contract.js.map +1 -1
  17. package/dist/fungible.d.ts.map +1 -1
  18. package/dist/fungible.js +38 -23
  19. package/dist/fungible.js.map +1 -1
  20. package/dist/generate/fungible.d.ts.map +1 -1
  21. package/dist/generate/fungible.js +1 -0
  22. package/dist/generate/fungible.js.map +1 -1
  23. package/dist/generate/non-fungible.d.ts.map +1 -1
  24. package/dist/generate/non-fungible.js +1 -0
  25. package/dist/generate/non-fungible.js.map +1 -1
  26. package/dist/generate/stablecoin.d.ts.map +1 -1
  27. package/dist/generate/stablecoin.js +1 -0
  28. package/dist/generate/stablecoin.js.map +1 -1
  29. package/dist/non-fungible.d.ts +1 -1
  30. package/dist/non-fungible.d.ts.map +1 -1
  31. package/dist/non-fungible.js +80 -41
  32. package/dist/non-fungible.js.map +1 -1
  33. package/dist/set-access-control.d.ts +2 -2
  34. package/dist/set-access-control.d.ts.map +1 -1
  35. package/dist/set-access-control.js +113 -10
  36. package/dist/set-access-control.js.map +1 -1
  37. package/dist/stablecoin.d.ts +2 -1
  38. package/dist/stablecoin.d.ts.map +1 -1
  39. package/dist/stablecoin.js +27 -6
  40. package/dist/stablecoin.js.map +1 -1
  41. package/dist/utils/compile-test.d.ts +1 -0
  42. package/dist/utils/compile-test.d.ts.map +1 -1
  43. package/dist/utils/compile-test.js +19 -5
  44. package/dist/utils/compile-test.js.map +1 -1
  45. package/dist/utils/version.d.ts +4 -4
  46. package/dist/utils/version.js +3 -3
  47. package/dist/zip-rust.d.ts.map +1 -1
  48. package/dist/zip-rust.js +4 -0
  49. package/dist/zip-rust.js.map +1 -1
  50. package/package.json +3 -3
  51. package/src/add-pausable.ts +29 -15
  52. package/src/add-upgradeable.ts +13 -6
  53. package/src/common-options.ts +3 -0
  54. package/src/contract.ts +9 -0
  55. package/src/fungible.ts +57 -29
  56. package/src/generate/fungible.ts +1 -0
  57. package/src/generate/non-fungible.ts +1 -0
  58. package/src/generate/stablecoin.ts +1 -0
  59. package/src/non-fungible.ts +121 -45
  60. package/src/set-access-control.ts +113 -9
  61. package/src/stablecoin.ts +39 -9
  62. package/src/utils/compile-test.ts +52 -25
  63. package/src/utils/version.ts +3 -3
  64. package/src/zip-rust.ts +5 -0
@@ -1,17 +1,17 @@
1
- import type { Contract } from './contract';
2
- import { ContractBuilder } from './contract';
3
- import { type Access, requireAccessControl, setAccessControl } from './set-access-control';
1
+ import { pickKeys } from '@openzeppelin/wizard-common';
4
2
  import { addPausable } from './add-pausable';
5
3
  import { addUpgradeable } from './add-upgradeable';
6
- import { defineFunctions } from './utils/define-functions';
7
4
  import type { CommonContractOptions } from './common-options';
8
- import { withCommonContractDefaults, getSelfArg } from './common-options';
9
- import { setInfo } from './set-info';
5
+ import { contractDefaults as commonDefaults, getSelfArg, withCommonContractDefaults } from './common-options';
6
+ import type { Contract } from './contract';
7
+ import { ContractBuilder } from './contract';
10
8
  import type { OptionsErrorMessages } from './error';
11
9
  import { OptionsError } from './error';
12
- import { contractDefaults as commonDefaults } from './common-options';
13
10
  import { printContract } from './print';
11
+ import { type Access, DEFAULT_ACCESS_CONTROL, requireAccessControl, setAccessControl } from './set-access-control';
12
+ import { setInfo } from './set-info';
14
13
  import { toByteArray } from './utils/convert-strings';
14
+ import { defineFunctions } from './utils/define-functions';
15
15
 
16
16
  export const defaults: Required<NonFungibleOptions> = {
17
17
  name: 'MyToken',
@@ -26,6 +26,7 @@ export const defaults: Required<NonFungibleOptions> = {
26
26
  sequential: false,
27
27
  access: commonDefaults.access, // TODO: Determine whether Access Control options should be visible in the UI before they are implemented as modules
28
28
  info: commonDefaults.info,
29
+ explicitImplementations: commonDefaults.explicitImplementations,
29
30
  } as const;
30
31
 
31
32
  export function printNonFungible(opts: NonFungibleOptions = defaults): string {
@@ -90,39 +91,60 @@ export function buildNonFungible(opts: NonFungibleOptions): Contract {
90
91
  throw new OptionsError(errors);
91
92
  }
92
93
 
93
- addBase(c, toByteArray(allOpts.name), toByteArray(allOpts.symbol), toByteArray(allOpts.tokenUri), allOpts.pausable);
94
+ addBase(
95
+ c,
96
+ toByteArray(allOpts.name),
97
+ toByteArray(allOpts.symbol),
98
+ toByteArray(allOpts.tokenUri),
99
+ allOpts.pausable,
100
+ allOpts.explicitImplementations,
101
+ );
94
102
 
95
103
  if (allOpts.pausable) {
96
- addPausable(c, allOpts.access);
104
+ addPausable(c, allOpts.access, allOpts.explicitImplementations);
97
105
  }
98
106
 
99
107
  if (allOpts.upgradeable) {
100
- addUpgradeable(c, allOpts.access);
108
+ addUpgradeable(c, allOpts.access, allOpts.explicitImplementations);
101
109
  }
102
110
 
103
111
  if (allOpts.burnable) {
104
- addBurnable(c, allOpts.pausable);
112
+ addBurnable(c, allOpts.pausable, allOpts.explicitImplementations);
105
113
  }
106
114
 
107
115
  if (allOpts.enumerable) {
108
- addEnumerable(c);
116
+ addEnumerable(c, allOpts.explicitImplementations);
109
117
  }
110
118
 
111
119
  if (allOpts.consecutive) {
112
- addConsecutive(c, allOpts.pausable, allOpts.access);
120
+ addConsecutive(c, allOpts.pausable, allOpts.access, allOpts.explicitImplementations);
113
121
  }
114
122
 
115
123
  if (allOpts.mintable) {
116
- addMintable(c, allOpts.enumerable, allOpts.pausable, allOpts.sequential, allOpts.access);
124
+ addMintable(
125
+ c,
126
+ allOpts.enumerable,
127
+ allOpts.pausable,
128
+ allOpts.sequential,
129
+ allOpts.access,
130
+ allOpts.explicitImplementations,
131
+ );
117
132
  }
118
133
 
119
- setAccessControl(c, allOpts.access);
134
+ setAccessControl(c, allOpts.access, allOpts.explicitImplementations);
120
135
  setInfo(c, allOpts.info);
121
136
 
122
137
  return c;
123
138
  }
124
139
 
125
- function addBase(c: ContractBuilder, name: string, symbol: string, tokenUri: string, pausable: boolean) {
140
+ function addBase(
141
+ c: ContractBuilder,
142
+ name: string,
143
+ symbol: string,
144
+ tokenUri: string,
145
+ pausable: boolean,
146
+ explicitImplementations: boolean,
147
+ ) {
126
148
  // Set metadata
127
149
  c.addConstructorCode(`let uri = String::from_str(e, "${tokenUri}");`);
128
150
  c.addConstructorCode(`let name = String::from_str(e, "${name}");`);
@@ -132,21 +154,24 @@ function addBase(c: ContractBuilder, name: string, symbol: string, tokenUri: str
132
154
  // Set token functions
133
155
  c.addUseClause('stellar_tokens::non_fungible', 'Base');
134
156
  c.addUseClause('stellar_tokens::non_fungible', 'NonFungibleToken');
135
- c.addUseClause('stellar_macros', 'default_impl');
157
+ if (explicitImplementations) c.addUseClause('stellar_tokens::non_fungible', 'ContractOverrides');
136
158
  c.addUseClause('soroban_sdk', 'contract');
137
159
  c.addUseClause('soroban_sdk', 'contractimpl');
138
160
  c.addUseClause('soroban_sdk', 'String');
139
161
  c.addUseClause('soroban_sdk', 'Env');
162
+ c.addUseClause('soroban_sdk', 'Address');
140
163
 
141
164
  const nonFungibleTokenTrait = {
142
165
  traitName: 'NonFungibleToken',
143
166
  structName: c.name,
144
- tags: ['default_impl', 'contractimpl'],
167
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
145
168
  assocType: 'type ContractType = Base;',
146
169
  };
147
170
 
148
171
  c.addTraitImplBlock(nonFungibleTokenTrait);
149
172
 
173
+ if (explicitImplementations) c.addTraitForEachFunctions(nonFungibleTokenTrait, nonFungibleTokenTraitFunctions);
174
+
150
175
  if (pausable) {
151
176
  c.addUseClause('stellar_macros', 'when_not_paused');
152
177
 
@@ -158,13 +183,13 @@ function addBase(c: ContractBuilder, name: string, symbol: string, tokenUri: str
158
183
  }
159
184
  }
160
185
 
161
- function addBurnable(c: ContractBuilder, pausable: boolean) {
186
+ function addBurnable(c: ContractBuilder, pausable: boolean, explicitImplementations: boolean) {
162
187
  c.addUseClause('stellar_tokens::non_fungible', 'burnable::NonFungibleBurnable');
163
188
 
164
189
  const nonFungibleBurnableTrait = {
165
190
  traitName: 'NonFungibleBurnable',
166
191
  structName: c.name,
167
- tags: ['contractimpl'],
192
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
168
193
  section: 'Extensions',
169
194
  };
170
195
 
@@ -176,32 +201,33 @@ function addBurnable(c: ContractBuilder, pausable: boolean) {
176
201
 
177
202
  c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn_from);
178
203
  c.addFunctionTag(burnableFunctions.burn_from, 'when_not_paused', nonFungibleBurnableTrait);
179
- } else {
180
- // prepend '#[default_impl]'
181
- nonFungibleBurnableTrait.tags.unshift('default_impl');
204
+ } else if (explicitImplementations)
205
+ c.addTraitForEachFunctions(nonFungibleBurnableTrait, nonFungibleBurnableFunctions);
206
+ else {
182
207
  c.addTraitImplBlock(nonFungibleBurnableTrait);
183
208
  }
184
209
  }
185
210
 
186
- function addEnumerable(c: ContractBuilder) {
211
+ function addEnumerable(c: ContractBuilder, explicitImplementations: boolean) {
187
212
  c.addUseClause('stellar_tokens::non_fungible', 'enumerable::{NonFungibleEnumerable, Enumerable}');
188
- c.addUseClause('stellar_macros', 'default_impl');
189
213
 
190
214
  const nonFungibleEnumerableTrait = {
191
215
  traitName: 'NonFungibleEnumerable',
192
216
  structName: c.name,
193
- tags: ['default_impl', 'contractimpl'],
217
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
194
218
  section: 'Extensions',
195
219
  };
196
- c.addTraitImplBlock(nonFungibleEnumerableTrait);
220
+ if (explicitImplementations)
221
+ c.addTraitForEachFunctions(nonFungibleEnumerableTrait, nonFungibleEnumerableTraitFunctions);
222
+ else c.addTraitImplBlock(nonFungibleEnumerableTrait);
197
223
 
198
224
  c.overrideAssocType('NonFungibleToken', 'type ContractType = Enumerable;');
199
225
  }
200
226
 
201
- function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) {
227
+ function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access, explicitImplementations: boolean) {
202
228
  c.addUseClause('stellar_tokens::non_fungible', 'consecutive::{NonFungibleConsecutive, Consecutive}');
203
- c.addUseClause('soroban_sdk', 'Address');
204
229
 
230
+ const effectiveAccess = access === false ? DEFAULT_ACCESS_CONTROL : access;
205
231
  const nonFungibleConsecutiveTrait = {
206
232
  traitName: 'NonFungibleConsecutive',
207
233
  structName: c.name,
@@ -213,45 +239,63 @@ function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) {
213
239
 
214
240
  c.overrideAssocType('NonFungibleToken', 'type ContractType = Consecutive;');
215
241
 
216
- const mintFn = access === 'ownable' ? consecutiveFunctions.batch_mint : consecutiveFunctions.batch_mint_with_caller;
242
+ const mintFn =
243
+ effectiveAccess === 'ownable' ? consecutiveFunctions.batch_mint : consecutiveFunctions.batch_mint_with_caller;
217
244
  c.addFreeFunction(mintFn);
218
245
  if (pausable) {
219
246
  c.addFunctionTag(mintFn, 'when_not_paused');
220
247
  }
221
248
 
222
- requireAccessControl(c, undefined, mintFn, access, {
223
- useMacro: true,
224
- role: 'minter',
225
- caller: 'caller',
226
- });
249
+ requireAccessControl(
250
+ c,
251
+ undefined,
252
+ mintFn,
253
+ effectiveAccess,
254
+ {
255
+ useMacro: true,
256
+ role: 'minter',
257
+ caller: 'caller',
258
+ },
259
+ explicitImplementations,
260
+ );
227
261
  }
228
262
 
229
- function addMintable(c: ContractBuilder, enumerable: boolean, pausable: boolean, sequential: boolean, access: Access) {
230
- c.addUseClause('soroban_sdk', 'Address');
263
+ function addMintable(
264
+ c: ContractBuilder,
265
+ enumerable: boolean,
266
+ pausable: boolean,
267
+ sequential: boolean,
268
+ access: Access,
269
+ explicitImplementations: boolean,
270
+ ) {
231
271
  const accessProps = { useMacro: true, role: 'minter', caller: 'caller' };
272
+ const effectiveAccess = access === false ? DEFAULT_ACCESS_CONTROL : access;
232
273
 
233
274
  let mintFn;
234
275
 
235
276
  if (enumerable) {
236
277
  if (sequential) {
237
278
  mintFn =
238
- access === 'ownable' ? enumerableFunctions.sequential_mint : enumerableFunctions.sequential_mint_with_caller;
279
+ effectiveAccess === 'ownable'
280
+ ? enumerableFunctions.sequential_mint
281
+ : enumerableFunctions.sequential_mint_with_caller;
239
282
  } else {
240
283
  mintFn =
241
- access === 'ownable'
284
+ effectiveAccess === 'ownable'
242
285
  ? enumerableFunctions.non_sequential_mint
243
286
  : enumerableFunctions.non_sequential_mint_with_caller;
244
287
  }
245
288
  } else {
246
289
  if (sequential) {
247
- mintFn = access === 'ownable' ? baseFunctions.sequential_mint : baseFunctions.sequential_mint_with_caller;
290
+ mintFn =
291
+ effectiveAccess === 'ownable' ? baseFunctions.sequential_mint : baseFunctions.sequential_mint_with_caller;
248
292
  } else {
249
- mintFn = access === 'ownable' ? baseFunctions.mint : baseFunctions.mint_with_caller;
293
+ mintFn = effectiveAccess === 'ownable' ? baseFunctions.mint : baseFunctions.mint_with_caller;
250
294
  }
251
295
  }
252
296
 
253
297
  c.addFreeFunction(mintFn);
254
- requireAccessControl(c, undefined, mintFn, access, accessProps);
298
+ requireAccessControl(c, undefined, mintFn, effectiveAccess, accessProps, explicitImplementations);
255
299
 
256
300
  if (pausable) {
257
301
  c.addFunctionTag(mintFn, 'when_not_paused');
@@ -361,6 +405,20 @@ const baseFunctions = defineFunctions({
361
405
  },
362
406
  });
363
407
 
408
+ const nonFungibleTokenTraitFunctions = pickKeys(baseFunctions, [
409
+ 'balance',
410
+ 'owner_of',
411
+ 'transfer',
412
+ 'transfer_from',
413
+ 'approve',
414
+ 'approve_for_all',
415
+ 'get_approved',
416
+ 'is_approved_for_all',
417
+ 'name',
418
+ 'symbol',
419
+ 'token_uri',
420
+ ]);
421
+
364
422
  const burnableFunctions = defineFunctions({
365
423
  burn: {
366
424
  args: [getSelfArg(), { name: 'from', type: 'Address' }, { name: 'token_id', type: 'u32' }],
@@ -377,11 +435,23 @@ const burnableFunctions = defineFunctions({
377
435
  },
378
436
  });
379
437
 
438
+ const nonFungibleBurnableFunctions = pickKeys(burnableFunctions, ['burn', 'burn_from']);
439
+
380
440
  const enumerableFunctions = defineFunctions({
381
441
  total_supply: {
382
442
  args: [getSelfArg()],
383
443
  returns: 'u32',
384
- code: ['non_fungible::enumerable::Enumerable::total_supply(e)'],
444
+ code: ['Enumerable::total_supply(e)'],
445
+ },
446
+ get_owner_token_id: {
447
+ args: [getSelfArg(), { name: 'owner', type: 'Address' }, { name: 'index', type: 'u32' }],
448
+ returns: 'u32',
449
+ code: ['Enumerable::get_owner_token_id(e, &owner, index)'],
450
+ },
451
+ get_token_id: {
452
+ args: [getSelfArg(), { name: 'index', type: 'u32' }],
453
+ returns: 'u32',
454
+ code: ['Enumerable::get_token_id(e, index)'],
385
455
  },
386
456
  non_sequential_mint: {
387
457
  name: 'mint',
@@ -410,12 +480,18 @@ const enumerableFunctions = defineFunctions({
410
480
  },
411
481
  });
412
482
 
483
+ const nonFungibleEnumerableTraitFunctions = pickKeys(enumerableFunctions, [
484
+ 'total_supply',
485
+ 'get_owner_token_id',
486
+ 'get_token_id',
487
+ ]);
488
+
413
489
  const consecutiveFunctions = defineFunctions({
414
490
  batch_mint: {
415
491
  name: 'batch_mint',
416
492
  args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'amount', type: 'u32' }],
417
493
  returns: 'u32',
418
- code: ['Consecutive::batch_mint(e, &to, amount);'],
494
+ code: ['Consecutive::batch_mint(e, &to, amount)'],
419
495
  },
420
496
  batch_mint_with_caller: {
421
497
  name: 'batch_mint',
@@ -426,6 +502,6 @@ const consecutiveFunctions = defineFunctions({
426
502
  { name: 'caller', type: 'Address' },
427
503
  ],
428
504
  returns: 'u32',
429
- code: ['Consecutive::batch_mint(e, &to, amount);'],
505
+ code: ['Consecutive::batch_mint(e, &to, amount)'],
430
506
  },
431
507
  });
@@ -1,4 +1,6 @@
1
+ import { getSelfArg } from './common-options';
1
2
  import type { BaseFunction, BaseTraitImplBlock, ContractBuilder } from './contract';
3
+ import { defineFunctions } from './utils/define-functions';
2
4
 
3
5
  export const accessOptions = [false, 'ownable', 'roles'] as const;
4
6
  export const DEFAULT_ACCESS_CONTROL = 'ownable';
@@ -13,7 +15,7 @@ export type AccessProps = {
13
15
  /**
14
16
  * Sets access control for the contract via constructor args.
15
17
  */
16
- export function setAccessControl(c: ContractBuilder, access: Access): void {
18
+ export function setAccessControl(c: ContractBuilder, access: Access, explicitImplementations = false): void {
17
19
  switch (access) {
18
20
  case false:
19
21
  break;
@@ -27,10 +29,14 @@ export function setAccessControl(c: ContractBuilder, access: Access): void {
27
29
  const ownableTrait = {
28
30
  traitName: 'Ownable',
29
31
  structName: c.name,
30
- tags: ['default_impl', 'contractimpl'],
32
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
31
33
  section: 'Utils',
32
34
  };
33
- c.addTraitImplBlock(ownableTrait);
35
+ if (explicitImplementations) {
36
+ c.addTraitForEachFunctions(ownableTrait, ownableFunctions);
37
+ } else {
38
+ c.addTraitImplBlock(ownableTrait);
39
+ }
34
40
 
35
41
  c.addConstructorArgument({ name: 'owner', type: 'Address' });
36
42
  c.addConstructorCode('ownable::set_owner(e, &owner);');
@@ -42,13 +48,18 @@ export function setAccessControl(c: ContractBuilder, access: Access): void {
42
48
  c.addUseClause('stellar_access::access_control', 'self', { alias: 'access_control' });
43
49
  c.addUseClause('stellar_access::access_control', 'AccessControl');
44
50
 
45
- const accessControltrait = {
51
+ const accessControlTrait = {
46
52
  traitName: 'AccessControl',
47
53
  structName: c.name,
48
- tags: ['default_impl', 'contractimpl'],
54
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
49
55
  section: 'Utils',
50
56
  };
51
- c.addTraitImplBlock(accessControltrait);
57
+ if (explicitImplementations) {
58
+ c.addUseClause('soroban_sdk', 'Symbol');
59
+ c.addTraitForEachFunctions(accessControlTrait, accessControlFunctions);
60
+ } else {
61
+ c.addTraitImplBlock(accessControlTrait);
62
+ }
52
63
 
53
64
  c.addConstructorArgument({ name: 'admin', type: 'Address' });
54
65
  c.addConstructorCode('access_control::set_admin(e, &admin);');
@@ -70,11 +81,12 @@ export function requireAccessControl(
70
81
  fn: BaseFunction,
71
82
  access: Access,
72
83
  accessProps: AccessProps = { useMacro: true },
84
+ explicitImplementations = false,
73
85
  ): void {
74
86
  if (access === false) {
75
87
  access = DEFAULT_ACCESS_CONTROL;
76
88
  }
77
- setAccessControl(c, access);
89
+ setAccessControl(c, access, explicitImplementations);
78
90
 
79
91
  switch (access) {
80
92
  case 'ownable': {
@@ -93,8 +105,9 @@ export function requireAccessControl(
93
105
 
94
106
  if (caller && role) {
95
107
  c.addUseClause('soroban_sdk', 'Symbol');
108
+ c.addUseClause('soroban_sdk', 'Vec');
96
109
  c.addConstructorArgument({ name: role, type: 'Address' });
97
- c.addConstructorCode(`access_control::grant_role_no_auth(e, &admin, &${role}, &Symbol::new(e, "${role}"));`);
110
+ c.addConstructorCode(`access_control::grant_role_no_auth(e, &${role}, &Symbol::new(e, "${role}"), &admin);`);
98
111
 
99
112
  if (useMacro) {
100
113
  c.addUseClause('stellar_macros', 'only_role');
@@ -102,7 +115,7 @@ export function requireAccessControl(
102
115
  } else {
103
116
  c.addFunctionCodeBefore(
104
117
  fn,
105
- [`access_control::ensure_role(e, ${caller}, &Symbol::new(e, "${role}"));`],
118
+ [`access_control::ensure_role(e, &Symbol::new(e, "${role}"), ${caller});`],
106
119
  trait,
107
120
  );
108
121
  }
@@ -123,3 +136,94 @@ export function requireAccessControl(
123
136
  }
124
137
  }
125
138
  }
139
+
140
+ const ownableFunctions = defineFunctions({
141
+ get_owner: {
142
+ args: [getSelfArg()],
143
+ returns: 'Option<Address>',
144
+ code: ['ownable::get_owner(e)'],
145
+ },
146
+ transfer_ownership: {
147
+ args: [getSelfArg(), { name: 'new_owner', type: 'Address' }, { name: 'live_until_ledger', type: 'u32' }],
148
+ code: ['ownable::transfer_ownership(e, &new_owner, live_until_ledger);'],
149
+ },
150
+ accept_ownership: {
151
+ args: [getSelfArg()],
152
+ code: ['ownable::accept_ownership(e);'],
153
+ },
154
+ renounce_ownership: {
155
+ args: [getSelfArg()],
156
+ code: ['ownable::renounce_ownership(e);'],
157
+ },
158
+ });
159
+
160
+ const accessControlFunctions = defineFunctions({
161
+ has_role: {
162
+ args: [getSelfArg(), { name: 'account', type: 'Address' }, { name: 'role', type: 'Symbol' }],
163
+ returns: 'Option<u32>',
164
+ code: ['access_control::has_role(e, &account, &role)'],
165
+ },
166
+ get_existing_roles: {
167
+ args: [getSelfArg()],
168
+ returns: 'Vec<Symbol>',
169
+ code: ['access_control::get_existing_roles(e)'],
170
+ },
171
+ get_role_member_count: {
172
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }],
173
+ returns: 'u32',
174
+ code: ['access_control::get_role_member_count(e, &role)'],
175
+ },
176
+ get_role_member: {
177
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }, { name: 'index', type: 'u32' }],
178
+ returns: 'Address',
179
+ code: ['access_control::get_role_member(e, &role, index)'],
180
+ },
181
+ get_role_admin: {
182
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }],
183
+ returns: 'Option<Symbol>',
184
+ code: ['access_control::get_role_admin(e, &role)'],
185
+ },
186
+ get_admin: {
187
+ args: [getSelfArg()],
188
+ returns: 'Option<Address>',
189
+ code: ['access_control::get_admin(e)'],
190
+ },
191
+ grant_role: {
192
+ args: [
193
+ getSelfArg(),
194
+ { name: 'account', type: 'Address' },
195
+ { name: 'role', type: 'Symbol' },
196
+ { name: 'caller', type: 'Address' },
197
+ ],
198
+ code: ['access_control::grant_role(e, &account, &role, &caller);'],
199
+ },
200
+ revoke_role: {
201
+ args: [
202
+ getSelfArg(),
203
+ { name: 'account', type: 'Address' },
204
+ { name: 'role', type: 'Symbol' },
205
+ { name: 'caller', type: 'Address' },
206
+ ],
207
+ code: ['access_control::revoke_role(e, &account, &role, &caller);'],
208
+ },
209
+ renounce_role: {
210
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }, { name: 'caller', type: 'Address' }],
211
+ code: ['access_control::renounce_role(e, &role, &caller);'],
212
+ },
213
+ transfer_admin_role: {
214
+ args: [getSelfArg(), { name: 'new_admin', type: 'Address' }, { name: 'live_until_ledger', type: 'u32' }],
215
+ code: ['access_control::transfer_admin_role(e, &new_admin, live_until_ledger);'],
216
+ },
217
+ accept_admin_transfer: {
218
+ args: [getSelfArg()],
219
+ code: ['access_control::accept_admin_transfer(e);'],
220
+ },
221
+ set_role_admin: {
222
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }, { name: 'admin_role', type: 'Symbol' }],
223
+ code: ['access_control::set_role_admin(e, &role, &admin_role);'],
224
+ },
225
+ renounce_admin: {
226
+ args: [getSelfArg()],
227
+ code: ['access_control::renounce_admin(e);'],
228
+ },
229
+ });
package/src/stablecoin.ts CHANGED
@@ -1,16 +1,16 @@
1
- import type { Contract, ContractBuilder } from './contract';
2
- import { defineFunctions } from './utils/define-functions';
3
1
  import { getSelfArg } from './common-options';
4
- import { printContract } from './print';
2
+ import type { Contract, ContractBuilder } from './contract';
5
3
  import type { FungibleOptions } from './fungible';
6
4
  import {
7
5
  buildFungible,
8
6
  defaults as fungibleDefaults,
9
- withDefaults as withFungibleDefaults,
10
7
  functions as fungibleFunctions,
11
8
  isAccessControlRequired as fungibleIsAccessControlRequired,
9
+ withDefaults as withFungibleDefaults,
12
10
  } from './fungible';
11
+ import { printContract } from './print';
13
12
  import { requireAccessControl, type Access } from './set-access-control';
13
+ import { defineFunctions } from './utils/define-functions';
14
14
 
15
15
  export const defaults: Required<StablecoinOptions> = {
16
16
  ...fungibleDefaults,
@@ -20,13 +20,14 @@ export const defaults: Required<StablecoinOptions> = {
20
20
  } as const;
21
21
 
22
22
  export const limitationsOptions = [false, 'allowlist', 'blocklist'] as const;
23
+ export type Limitations = (typeof limitationsOptions)[number];
23
24
 
24
25
  export function printStablecoin(opts: StablecoinOptions = defaults): string {
25
26
  return printContract(buildStablecoin(opts));
26
27
  }
27
28
 
28
29
  export interface StablecoinOptions extends FungibleOptions {
29
- limitations?: false | 'allowlist' | 'blocklist';
30
+ limitations?: Limitations;
30
31
  }
31
32
 
32
33
  function withDefaults(opts: StablecoinOptions): Required<StablecoinOptions> {
@@ -48,13 +49,18 @@ export function buildStablecoin(opts: StablecoinOptions): Contract {
48
49
  const c = buildFungible(allOpts);
49
50
 
50
51
  if (allOpts.limitations) {
51
- addLimitations(c, allOpts.access, allOpts.limitations);
52
+ addLimitations(c, allOpts.access, allOpts.limitations, allOpts.explicitImplementations);
52
53
  }
53
54
 
54
55
  return c;
55
56
  }
56
57
 
57
- function addLimitations(c: ContractBuilder, access: Access, mode: 'allowlist' | 'blocklist') {
58
+ function addLimitations(
59
+ c: ContractBuilder,
60
+ access: Access,
61
+ mode: 'allowlist' | 'blocklist',
62
+ explicitImplementations: boolean,
63
+ ) {
58
64
  const type = mode === 'allowlist';
59
65
 
60
66
  const limitationsTrait = {
@@ -72,6 +78,8 @@ function addLimitations(c: ContractBuilder, access: Access, mode: 'allowlist' |
72
78
  c.overrideAssocType('FungibleToken', 'type ContractType = BlockList;');
73
79
  }
74
80
 
81
+ if (explicitImplementations) overrideFungibleReadonlyFunctionsWithBase(c);
82
+
75
83
  const [getterFn, addFn, removeFn] = type
76
84
  ? [functions.allowed, functions.allow_user, functions.disallow_user]
77
85
  : [functions.blocked, functions.block_user, functions.unblock_user];
@@ -85,10 +93,32 @@ function addLimitations(c: ContractBuilder, access: Access, mode: 'allowlist' |
85
93
  };
86
94
 
87
95
  c.addTraitFunction(limitationsTrait, addFn);
88
- requireAccessControl(c, limitationsTrait, addFn, access, accessProps);
96
+ requireAccessControl(c, limitationsTrait, addFn, access, accessProps, explicitImplementations);
89
97
 
90
98
  c.addTraitFunction(limitationsTrait, removeFn);
91
- requireAccessControl(c, limitationsTrait, removeFn, access, accessProps);
99
+ requireAccessControl(c, limitationsTrait, removeFn, access, accessProps, explicitImplementations);
100
+ }
101
+
102
+ function overrideFungibleReadonlyFunctionsWithBase(c: ContractBuilder) {
103
+ c.addUseClause('stellar_tokens::fungible', 'ContractOverrides');
104
+ const fungibleTokenTrait = {
105
+ traitName: 'FungibleToken',
106
+ structName: c.name,
107
+ tags: [],
108
+ };
109
+
110
+ const overrides: Array<[(typeof fungibleFunctions)[keyof typeof fungibleFunctions], string[]]> = [
111
+ [fungibleFunctions.total_supply, ['Self::ContractType::total_supply(e)']],
112
+ [fungibleFunctions.balance, ['Self::ContractType::balance(e, &account)']],
113
+ [fungibleFunctions.allowance, ['Self::ContractType::allowance(e, &owner, &spender)']],
114
+ [fungibleFunctions.decimals, ['Self::ContractType::decimals(e)']],
115
+ [fungibleFunctions.name, ['Self::ContractType::name(e)']],
116
+ [fungibleFunctions.symbol, ['Self::ContractType::symbol(e)']],
117
+ ];
118
+
119
+ for (const [fn, code] of overrides) {
120
+ c.setFunctionCode(fn, code, fungibleTokenTrait);
121
+ }
92
122
  }
93
123
 
94
124
  const functions = {