@openzeppelin/wizard-stellar 0.4.4 → 0.4.5

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 (61) 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 +10 -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 +35 -14
  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 +86 -36
  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 +105 -8
  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/zip-rust.d.ts.map +1 -1
  46. package/dist/zip-rust.js +4 -0
  47. package/dist/zip-rust.js.map +1 -1
  48. package/package.json +3 -3
  49. package/src/add-pausable.ts +30 -15
  50. package/src/add-upgradeable.ts +13 -6
  51. package/src/common-options.ts +3 -0
  52. package/src/contract.ts +9 -0
  53. package/src/fungible.ts +53 -19
  54. package/src/generate/fungible.ts +1 -0
  55. package/src/generate/non-fungible.ts +1 -0
  56. package/src/generate/stablecoin.ts +1 -0
  57. package/src/non-fungible.ts +123 -40
  58. package/src/set-access-control.ts +105 -7
  59. package/src/stablecoin.ts +39 -9
  60. package/src/utils/compile-test.ts +52 -25
  61. 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,25 @@ 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');
158
+ else c.addUseClause('stellar_macros', 'default_impl');
136
159
  c.addUseClause('soroban_sdk', 'contract');
137
160
  c.addUseClause('soroban_sdk', 'contractimpl');
138
161
  c.addUseClause('soroban_sdk', 'String');
139
162
  c.addUseClause('soroban_sdk', 'Env');
163
+ if (explicitImplementations || pausable) c.addUseClause('soroban_sdk', 'Address');
140
164
 
141
165
  const nonFungibleTokenTrait = {
142
166
  traitName: 'NonFungibleToken',
143
167
  structName: c.name,
144
- tags: ['default_impl', 'contractimpl'],
168
+ tags: explicitImplementations ? ['contractimpl'] : ['default_impl', 'contractimpl'],
145
169
  assocType: 'type ContractType = Base;',
146
170
  };
147
171
 
148
172
  c.addTraitImplBlock(nonFungibleTokenTrait);
149
173
 
174
+ if (explicitImplementations) c.addTraitForEachFunctions(nonFungibleTokenTrait, nonFungibleTokenTraitFunctions);
175
+
150
176
  if (pausable) {
151
177
  c.addUseClause('stellar_macros', 'when_not_paused');
152
178
 
@@ -158,7 +184,7 @@ function addBase(c: ContractBuilder, name: string, symbol: string, tokenUri: str
158
184
  }
159
185
  }
160
186
 
161
- function addBurnable(c: ContractBuilder, pausable: boolean) {
187
+ function addBurnable(c: ContractBuilder, pausable: boolean, explicitImplementations: boolean) {
162
188
  c.addUseClause('stellar_tokens::non_fungible', 'burnable::NonFungibleBurnable');
163
189
 
164
190
  const nonFungibleBurnableTrait = {
@@ -176,32 +202,38 @@ function addBurnable(c: ContractBuilder, pausable: boolean) {
176
202
 
177
203
  c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn_from);
178
204
  c.addFunctionTag(burnableFunctions.burn_from, 'when_not_paused', nonFungibleBurnableTrait);
179
- } else {
205
+ } else if (explicitImplementations)
206
+ c.addTraitForEachFunctions(nonFungibleBurnableTrait, nonFungibleBurnableFunctions);
207
+ else {
180
208
  // prepend '#[default_impl]'
181
209
  nonFungibleBurnableTrait.tags.unshift('default_impl');
182
210
  c.addTraitImplBlock(nonFungibleBurnableTrait);
183
211
  }
184
212
  }
185
213
 
186
- function addEnumerable(c: ContractBuilder) {
214
+ function addEnumerable(c: ContractBuilder, explicitImplementations: boolean) {
187
215
  c.addUseClause('stellar_tokens::non_fungible', 'enumerable::{NonFungibleEnumerable, Enumerable}');
188
- c.addUseClause('stellar_macros', 'default_impl');
216
+ if (explicitImplementations) c.addUseClause('soroban_sdk', 'Address');
217
+ else c.addUseClause('stellar_macros', 'default_impl');
189
218
 
190
219
  const nonFungibleEnumerableTrait = {
191
220
  traitName: 'NonFungibleEnumerable',
192
221
  structName: c.name,
193
- tags: ['default_impl', 'contractimpl'],
222
+ tags: explicitImplementations ? ['contractimpl'] : ['default_impl', 'contractimpl'],
194
223
  section: 'Extensions',
195
224
  };
196
- c.addTraitImplBlock(nonFungibleEnumerableTrait);
225
+ if (explicitImplementations)
226
+ c.addTraitForEachFunctions(nonFungibleEnumerableTrait, nonFungibleEnumerableTraitFunctions);
227
+ else c.addTraitImplBlock(nonFungibleEnumerableTrait);
197
228
 
198
229
  c.overrideAssocType('NonFungibleToken', 'type ContractType = Enumerable;');
199
230
  }
200
231
 
201
- function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) {
232
+ function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access, explicitImplementations: boolean) {
202
233
  c.addUseClause('stellar_tokens::non_fungible', 'consecutive::{NonFungibleConsecutive, Consecutive}');
203
234
  c.addUseClause('soroban_sdk', 'Address');
204
235
 
236
+ const effectiveAccess = access === false ? DEFAULT_ACCESS_CONTROL : access;
205
237
  const nonFungibleConsecutiveTrait = {
206
238
  traitName: 'NonFungibleConsecutive',
207
239
  structName: c.name,
@@ -213,45 +245,64 @@ function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) {
213
245
 
214
246
  c.overrideAssocType('NonFungibleToken', 'type ContractType = Consecutive;');
215
247
 
216
- const mintFn = access === 'ownable' ? consecutiveFunctions.batch_mint : consecutiveFunctions.batch_mint_with_caller;
248
+ const mintFn =
249
+ effectiveAccess === 'ownable' ? consecutiveFunctions.batch_mint : consecutiveFunctions.batch_mint_with_caller;
217
250
  c.addFreeFunction(mintFn);
218
251
  if (pausable) {
219
252
  c.addFunctionTag(mintFn, 'when_not_paused');
220
253
  }
221
254
 
222
- requireAccessControl(c, undefined, mintFn, access, {
223
- useMacro: true,
224
- role: 'minter',
225
- caller: 'caller',
226
- });
255
+ requireAccessControl(
256
+ c,
257
+ undefined,
258
+ mintFn,
259
+ effectiveAccess,
260
+ {
261
+ useMacro: true,
262
+ role: 'minter',
263
+ caller: 'caller',
264
+ },
265
+ explicitImplementations,
266
+ );
227
267
  }
228
268
 
229
- function addMintable(c: ContractBuilder, enumerable: boolean, pausable: boolean, sequential: boolean, access: Access) {
269
+ function addMintable(
270
+ c: ContractBuilder,
271
+ enumerable: boolean,
272
+ pausable: boolean,
273
+ sequential: boolean,
274
+ access: Access,
275
+ explicitImplementations: boolean,
276
+ ) {
230
277
  c.addUseClause('soroban_sdk', 'Address');
231
278
  const accessProps = { useMacro: true, role: 'minter', caller: 'caller' };
279
+ const effectiveAccess = access === false ? DEFAULT_ACCESS_CONTROL : access;
232
280
 
233
281
  let mintFn;
234
282
 
235
283
  if (enumerable) {
236
284
  if (sequential) {
237
285
  mintFn =
238
- access === 'ownable' ? enumerableFunctions.sequential_mint : enumerableFunctions.sequential_mint_with_caller;
286
+ effectiveAccess === 'ownable'
287
+ ? enumerableFunctions.sequential_mint
288
+ : enumerableFunctions.sequential_mint_with_caller;
239
289
  } else {
240
290
  mintFn =
241
- access === 'ownable'
291
+ effectiveAccess === 'ownable'
242
292
  ? enumerableFunctions.non_sequential_mint
243
293
  : enumerableFunctions.non_sequential_mint_with_caller;
244
294
  }
245
295
  } else {
246
296
  if (sequential) {
247
- mintFn = access === 'ownable' ? baseFunctions.sequential_mint : baseFunctions.sequential_mint_with_caller;
297
+ mintFn =
298
+ effectiveAccess === 'ownable' ? baseFunctions.sequential_mint : baseFunctions.sequential_mint_with_caller;
248
299
  } else {
249
- mintFn = access === 'ownable' ? baseFunctions.mint : baseFunctions.mint_with_caller;
300
+ mintFn = effectiveAccess === 'ownable' ? baseFunctions.mint : baseFunctions.mint_with_caller;
250
301
  }
251
302
  }
252
303
 
253
304
  c.addFreeFunction(mintFn);
254
- requireAccessControl(c, undefined, mintFn, access, accessProps);
305
+ requireAccessControl(c, undefined, mintFn, effectiveAccess, accessProps, explicitImplementations);
255
306
 
256
307
  if (pausable) {
257
308
  c.addFunctionTag(mintFn, 'when_not_paused');
@@ -361,6 +412,20 @@ const baseFunctions = defineFunctions({
361
412
  },
362
413
  });
363
414
 
415
+ const nonFungibleTokenTraitFunctions = pickKeys(baseFunctions, [
416
+ 'balance',
417
+ 'owner_of',
418
+ 'transfer',
419
+ 'transfer_from',
420
+ 'approve',
421
+ 'approve_for_all',
422
+ 'get_approved',
423
+ 'is_approved_for_all',
424
+ 'name',
425
+ 'symbol',
426
+ 'token_uri',
427
+ ]);
428
+
364
429
  const burnableFunctions = defineFunctions({
365
430
  burn: {
366
431
  args: [getSelfArg(), { name: 'from', type: 'Address' }, { name: 'token_id', type: 'u32' }],
@@ -377,11 +442,23 @@ const burnableFunctions = defineFunctions({
377
442
  },
378
443
  });
379
444
 
445
+ const nonFungibleBurnableFunctions = pickKeys(burnableFunctions, ['burn', 'burn_from']);
446
+
380
447
  const enumerableFunctions = defineFunctions({
381
448
  total_supply: {
382
449
  args: [getSelfArg()],
383
450
  returns: 'u32',
384
- code: ['non_fungible::enumerable::Enumerable::total_supply(e)'],
451
+ code: ['Enumerable::total_supply(e)'],
452
+ },
453
+ get_owner_token_id: {
454
+ args: [getSelfArg(), { name: 'owner', type: 'Address' }, { name: 'index', type: 'u32' }],
455
+ returns: 'u32',
456
+ code: ['Enumerable::get_owner_token_id(e, &owner, index)'],
457
+ },
458
+ get_token_id: {
459
+ args: [getSelfArg(), { name: 'index', type: 'u32' }],
460
+ returns: 'u32',
461
+ code: ['Enumerable::get_token_id(e, index)'],
385
462
  },
386
463
  non_sequential_mint: {
387
464
  name: 'mint',
@@ -410,12 +487,18 @@ const enumerableFunctions = defineFunctions({
410
487
  },
411
488
  });
412
489
 
490
+ const nonFungibleEnumerableTraitFunctions = pickKeys(enumerableFunctions, [
491
+ 'total_supply',
492
+ 'get_owner_token_id',
493
+ 'get_token_id',
494
+ ]);
495
+
413
496
  const consecutiveFunctions = defineFunctions({
414
497
  batch_mint: {
415
498
  name: 'batch_mint',
416
499
  args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'amount', type: 'u32' }],
417
500
  returns: 'u32',
418
- code: ['Consecutive::batch_mint(e, &to, amount);'],
501
+ code: ['Consecutive::batch_mint(e, &to, amount)'],
419
502
  },
420
503
  batch_mint_with_caller: {
421
504
  name: 'batch_mint',
@@ -426,6 +509,6 @@ const consecutiveFunctions = defineFunctions({
426
509
  { name: 'caller', type: 'Address' },
427
510
  ],
428
511
  returns: 'u32',
429
- code: ['Consecutive::batch_mint(e, &to, amount);'],
512
+ code: ['Consecutive::batch_mint(e, &to, amount)'],
430
513
  },
431
514
  });
@@ -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'] : ['default_impl', 'contractimpl'],
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'] : ['default_impl', 'contractimpl'],
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': {
@@ -123,3 +135,89 @@ export function requireAccessControl(
123
135
  }
124
136
  }
125
137
  }
138
+
139
+ const ownableFunctions = defineFunctions({
140
+ get_owner: {
141
+ args: [getSelfArg()],
142
+ returns: 'Option<Address>',
143
+ code: ['ownable::get_owner(e)'],
144
+ },
145
+ transfer_ownership: {
146
+ args: [getSelfArg(), { name: 'new_owner', type: 'Address' }, { name: 'live_until_ledger', type: 'u32' }],
147
+ code: ['ownable::transfer_ownership(e, &new_owner, live_until_ledger);'],
148
+ },
149
+ accept_ownership: {
150
+ args: [getSelfArg()],
151
+ code: ['ownable::accept_ownership(e);'],
152
+ },
153
+ renounce_ownership: {
154
+ args: [getSelfArg()],
155
+ code: ['ownable::renounce_ownership(e);'],
156
+ },
157
+ });
158
+
159
+ const accessControlFunctions = defineFunctions({
160
+ has_role: {
161
+ args: [getSelfArg(), { name: 'account', type: 'Address' }, { name: 'role', type: 'Symbol' }],
162
+ returns: 'Option<u32>',
163
+ code: ['access_control::has_role(e, &account, &role)'],
164
+ },
165
+ get_role_member_count: {
166
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }],
167
+ returns: 'u32',
168
+ code: ['access_control::get_role_member_count(e, &role)'],
169
+ },
170
+ get_role_member: {
171
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }, { name: 'index', type: 'u32' }],
172
+ returns: 'Address',
173
+ code: ['access_control::get_role_member(e, &role, index)'],
174
+ },
175
+ get_role_admin: {
176
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }],
177
+ returns: 'Option<Symbol>',
178
+ code: ['access_control::get_role_admin(e, &role)'],
179
+ },
180
+ get_admin: {
181
+ args: [getSelfArg()],
182
+ returns: 'Option<Address>',
183
+ code: ['access_control::get_admin(e)'],
184
+ },
185
+ grant_role: {
186
+ args: [
187
+ getSelfArg(),
188
+ { name: 'caller', type: 'Address' },
189
+ { name: 'account', type: 'Address' },
190
+ { name: 'role', type: 'Symbol' },
191
+ ],
192
+ code: ['access_control::grant_role(e, &caller, &account, &role);'],
193
+ },
194
+ revoke_role: {
195
+ args: [
196
+ getSelfArg(),
197
+ { name: 'caller', type: 'Address' },
198
+ { name: 'account', type: 'Address' },
199
+ { name: 'role', type: 'Symbol' },
200
+ ],
201
+ code: ['access_control::revoke_role(e, &caller, &account, &role);'],
202
+ },
203
+ renounce_role: {
204
+ args: [getSelfArg(), { name: 'caller', type: 'Address' }, { name: 'role', type: 'Symbol' }],
205
+ code: ['access_control::renounce_role(e, &caller, &role);'],
206
+ },
207
+ transfer_admin_role: {
208
+ args: [getSelfArg(), { name: 'new_admin', type: 'Address' }, { name: 'live_until_ledger', type: 'u32' }],
209
+ code: ['access_control::transfer_admin_role(e, &new_admin, live_until_ledger);'],
210
+ },
211
+ accept_admin_transfer: {
212
+ args: [getSelfArg()],
213
+ code: ['access_control::accept_admin_transfer(e);'],
214
+ },
215
+ set_role_admin: {
216
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }, { name: 'admin_role', type: 'Symbol' }],
217
+ code: ['access_control::set_role_admin(e, &role, &admin_role);'],
218
+ },
219
+ renounce_admin: {
220
+ args: [getSelfArg()],
221
+ code: ['access_control::renounce_admin(e);'],
222
+ },
223
+ });
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 = {