@openzeppelin/wizard-stellar 0.4.3 → 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 (71) hide show
  1. package/README.md +2 -2
  2. package/dist/add-pausable.d.ts +1 -1
  3. package/dist/add-pausable.d.ts.map +1 -1
  4. package/dist/add-pausable.js +10 -8
  5. package/dist/add-pausable.js.map +1 -1
  6. package/dist/add-upgradeable.d.ts +1 -1
  7. package/dist/add-upgradeable.d.ts.map +1 -1
  8. package/dist/add-upgradeable.js +2 -2
  9. package/dist/add-upgradeable.js.map +1 -1
  10. package/dist/common-options.d.ts +1 -0
  11. package/dist/common-options.d.ts.map +1 -1
  12. package/dist/common-options.js +4 -4
  13. package/dist/common-options.js.map +1 -1
  14. package/dist/contract.d.ts +2 -0
  15. package/dist/contract.d.ts.map +1 -1
  16. package/dist/contract.js +11 -7
  17. package/dist/contract.js.map +1 -1
  18. package/dist/fungible.d.ts +1 -1
  19. package/dist/fungible.d.ts.map +1 -1
  20. package/dist/fungible.js +43 -24
  21. package/dist/fungible.js.map +1 -1
  22. package/dist/generate/alternatives.js +1 -1
  23. package/dist/generate/alternatives.js.map +1 -1
  24. package/dist/generate/fungible.d.ts.map +1 -1
  25. package/dist/generate/fungible.js +1 -0
  26. package/dist/generate/fungible.js.map +1 -1
  27. package/dist/generate/non-fungible.d.ts.map +1 -1
  28. package/dist/generate/non-fungible.js +2 -0
  29. package/dist/generate/non-fungible.js.map +1 -1
  30. package/dist/generate/stablecoin.d.ts.map +1 -1
  31. package/dist/generate/stablecoin.js +1 -0
  32. package/dist/generate/stablecoin.js.map +1 -1
  33. package/dist/non-fungible.d.ts +2 -1
  34. package/dist/non-fungible.d.ts.map +1 -1
  35. package/dist/non-fungible.js +96 -45
  36. package/dist/non-fungible.js.map +1 -1
  37. package/dist/print.js +3 -6
  38. package/dist/print.js.map +1 -1
  39. package/dist/set-access-control.d.ts +2 -2
  40. package/dist/set-access-control.d.ts.map +1 -1
  41. package/dist/set-access-control.js +105 -8
  42. package/dist/set-access-control.js.map +1 -1
  43. package/dist/stablecoin.d.ts +2 -1
  44. package/dist/stablecoin.d.ts.map +1 -1
  45. package/dist/stablecoin.js +30 -10
  46. package/dist/stablecoin.js.map +1 -1
  47. package/dist/utils/compile-test.d.ts +1 -0
  48. package/dist/utils/compile-test.d.ts.map +1 -1
  49. package/dist/utils/compile-test.js +20 -6
  50. package/dist/utils/compile-test.js.map +1 -1
  51. package/dist/utils/define-functions.js +4 -7
  52. package/dist/utils/define-functions.js.map +1 -1
  53. package/dist/zip-rust.d.ts.map +1 -1
  54. package/dist/zip-rust.js +5 -1
  55. package/dist/zip-rust.js.map +1 -1
  56. package/dist/zip-shared.js +1 -1
  57. package/dist/zip-shared.js.map +1 -1
  58. package/package.json +3 -3
  59. package/src/add-pausable.ts +30 -15
  60. package/src/add-upgradeable.ts +13 -6
  61. package/src/common-options.ts +3 -0
  62. package/src/contract.ts +9 -0
  63. package/src/fungible.ts +54 -20
  64. package/src/generate/fungible.ts +1 -0
  65. package/src/generate/non-fungible.ts +2 -0
  66. package/src/generate/stablecoin.ts +1 -0
  67. package/src/non-fungible.ts +127 -41
  68. package/src/set-access-control.ts +105 -7
  69. package/src/stablecoin.ts +39 -9
  70. package/src/utils/compile-test.ts +52 -25
  71. package/src/zip-rust.ts +5 -0
@@ -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 = {
@@ -1,14 +1,14 @@
1
- import { tmpdir } from 'os';
2
1
  import type { ExecutionContext } from 'ava';
2
+ import { exec } from 'child_process';
3
+ import { mkdir, mkdtemp, rm } from 'fs/promises';
4
+ import { tmpdir } from 'os';
3
5
  import path from 'path';
4
6
  import { promisify } from 'util';
5
- import { exec } from 'child_process';
6
7
  import type { GenericOptions } from '../build-generic';
7
8
  import type { Contract } from '../contract';
8
- import { assertLayout, snapshotZipContents, expandPathsFromFilesPaths, extractPackage } from './zip-test';
9
- import { mkdtemp, rm } from 'fs/promises';
10
- import { contractOptionsToContractName } from '../zip-shared';
11
9
  import { zipRustProject } from '../zip-rust';
10
+ import { contractOptionsToContractName } from '../zip-shared';
11
+ import { assertLayout, expandPathsFromFilesPaths, extractPackage, snapshotZipContents } from './zip-test';
12
12
 
13
13
  const asyncExec = promisify(exec);
14
14
 
@@ -29,7 +29,6 @@ export const withTemporaryFolderDo =
29
29
  (...testFunctionArguments: Args) =>
30
30
  async (test: ExecutionContext) => {
31
31
  const temporaryFolder = await mkdtemp(path.join(tmpdir(), `compilation-test-${crypto.randomUUID()}`));
32
-
33
32
  try {
34
33
  await testFunction(...testFunctionArguments, test, temporaryFolder);
35
34
  } finally {
@@ -37,33 +36,61 @@ export const withTemporaryFolderDo =
37
36
  }
38
37
  };
39
38
 
39
+ const doRunRustCompilationTest = async (
40
+ makeContract: MakeContract,
41
+ opts: GenericOptions,
42
+ testOptions: { snapshotResult: boolean },
43
+ test: ExecutionContext,
44
+ folderPath: string,
45
+ ) => {
46
+ test.timeout(3_000_000);
47
+
48
+ await mkdir(folderPath, { recursive: true });
49
+
50
+ const scaffoldContractName = contractOptionsToContractName(opts?.kind || 'contract');
51
+
52
+ const expectedZipFiles = [
53
+ `contracts/${scaffoldContractName}/src/contract.rs`,
54
+ `contracts/${scaffoldContractName}/src/test.rs`,
55
+ `contracts/${scaffoldContractName}/src/lib.rs`,
56
+ `contracts/${scaffoldContractName}/Cargo.toml`,
57
+ 'Cargo.toml',
58
+ '.gitignore',
59
+ 'README.md',
60
+ ];
61
+
62
+ const zip = await zipRustProject(makeContract(opts), opts);
63
+
64
+ assertLayout(test, zip, expandPathsFromFilesPaths(expectedZipFiles));
65
+ await extractPackage(zip, folderPath);
66
+ await runCargoTest(test, folderPath);
67
+
68
+ if (testOptions.snapshotResult) await snapshotZipContents(test, zip, expectedZipFiles);
69
+ };
70
+
40
71
  export const runRustCompilationTest = withTemporaryFolderDo(
41
72
  async (
42
73
  makeContract: MakeContract,
43
74
  opts: GenericOptions,
44
- testOptions: { snapshotResult: boolean },
75
+ testOptions: { snapshotResult: boolean; excludeExplicitTraitTest?: boolean },
45
76
  test: ExecutionContext,
46
77
  folderPath: string,
47
78
  ) => {
48
- test.timeout(3_000_000);
79
+ await doRunRustCompilationTest(makeContract, opts, testOptions, test, `${folderPath}/default`);
49
80
 
50
- const scaffoldContractName = contractOptionsToContractName(opts?.kind || 'contract');
81
+ const shouldBeExcludedOrHasAlreadyRun = testOptions.excludeExplicitTraitTest || opts.explicitImplementations;
82
+ if (shouldBeExcludedOrHasAlreadyRun) return;
51
83
 
52
- const expectedZipFiles = [
53
- `contracts/${scaffoldContractName}/src/contract.rs`,
54
- `contracts/${scaffoldContractName}/src/test.rs`,
55
- `contracts/${scaffoldContractName}/src/lib.rs`,
56
- `contracts/${scaffoldContractName}/Cargo.toml`,
57
- 'Cargo.toml',
58
- 'README.md',
59
- ];
60
-
61
- const zip = await zipRustProject(makeContract(opts), opts);
62
-
63
- assertLayout(test, zip, expandPathsFromFilesPaths(expectedZipFiles));
64
- await extractPackage(zip, folderPath);
65
- await runCargoTest(test, folderPath);
66
-
67
- if (testOptions.snapshotResult) await snapshotZipContents(test, zip, expectedZipFiles);
84
+ try {
85
+ await doRunRustCompilationTest(
86
+ makeContract,
87
+ { ...opts, explicitImplementations: true },
88
+ testOptions,
89
+ test,
90
+ `${folderPath}/explicit`,
91
+ );
92
+ } catch (error) {
93
+ throw new Error(`EXPLICIT IMPLEMENTATION ERROR: ${error}`);
94
+ }
68
95
  },
69
96
  );
package/src/zip-rust.ts CHANGED
@@ -25,6 +25,10 @@ Continue your development journey with [Stellar CLI](https://github.com/stellar/
25
25
  - See [Git installation guide](https://github.com/git-guides/install-git).
26
26
  `;
27
27
 
28
+ const gitIgnore = `\
29
+ target/
30
+ `;
31
+
28
32
  export const createRustZipEnvironment = (c: Contract, opts: GenericOptions) => {
29
33
  const zip = new JSZip();
30
34
 
@@ -35,6 +39,7 @@ export const createRustZipEnvironment = (c: Contract, opts: GenericOptions) => {
35
39
  zip.file(`contracts/${contractName}/src/lib.rs`, createRustLibFile);
36
40
  zip.file(`contracts/${contractName}/Cargo.toml`, printContractCargo(contractName));
37
41
  zip.file('Cargo.toml', workspaceCargo);
42
+ zip.file('.gitignore', gitIgnore);
38
43
 
39
44
  return zip;
40
45
  };