@openzeppelin/wizard-stellar 0.4.5 → 0.6.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 (100) hide show
  1. package/dist/add-pausable.d.ts.map +1 -1
  2. package/dist/add-pausable.js +0 -2
  3. package/dist/add-pausable.js.map +1 -1
  4. package/dist/add-upgradeable.d.ts.map +1 -1
  5. package/dist/add-upgradeable.js +15 -14
  6. package/dist/add-upgradeable.js.map +1 -1
  7. package/dist/api.d.ts +5 -2
  8. package/dist/api.d.ts.map +1 -1
  9. package/dist/api.js +7 -1
  10. package/dist/api.js.map +1 -1
  11. package/dist/build-generic.d.ts +4 -0
  12. package/dist/build-generic.d.ts.map +1 -1
  13. package/dist/build-generic.js +3 -0
  14. package/dist/build-generic.js.map +1 -1
  15. package/dist/contract.d.ts +3 -0
  16. package/dist/contract.d.ts.map +1 -1
  17. package/dist/contract.js +4 -0
  18. package/dist/contract.js.map +1 -1
  19. package/dist/fungible.d.ts +1 -0
  20. package/dist/fungible.d.ts.map +1 -1
  21. package/dist/fungible.js +83 -40
  22. package/dist/fungible.js.map +1 -1
  23. package/dist/generate/fungible.d.ts.map +1 -1
  24. package/dist/generate/fungible.js +1 -0
  25. package/dist/generate/fungible.js.map +1 -1
  26. package/dist/generate/governor.d.ts +3 -0
  27. package/dist/generate/governor.d.ts.map +1 -0
  28. package/dist/generate/governor.js +24 -0
  29. package/dist/generate/governor.js.map +1 -0
  30. package/dist/generate/non-fungible.d.ts.map +1 -1
  31. package/dist/generate/non-fungible.js +1 -0
  32. package/dist/generate/non-fungible.js.map +1 -1
  33. package/dist/generate/sources.d.ts.map +1 -1
  34. package/dist/generate/sources.js +6 -0
  35. package/dist/generate/sources.js.map +1 -1
  36. package/dist/generate/stablecoin.d.ts.map +1 -1
  37. package/dist/generate/stablecoin.js +1 -0
  38. package/dist/generate/stablecoin.js.map +1 -1
  39. package/dist/governor.d.ts +17 -0
  40. package/dist/governor.d.ts.map +1 -0
  41. package/dist/governor.js +378 -0
  42. package/dist/governor.js.map +1 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +5 -3
  46. package/dist/index.js.map +1 -1
  47. package/dist/kind.js +1 -0
  48. package/dist/kind.js.map +1 -1
  49. package/dist/non-fungible.d.ts +1 -0
  50. package/dist/non-fungible.d.ts.map +1 -1
  51. package/dist/non-fungible.js +72 -22
  52. package/dist/non-fungible.js.map +1 -1
  53. package/dist/print.d.ts.map +1 -1
  54. package/dist/print.js +6 -1
  55. package/dist/print.js.map +1 -1
  56. package/dist/set-access-control.d.ts.map +1 -1
  57. package/dist/set-access-control.js +16 -10
  58. package/dist/set-access-control.js.map +1 -1
  59. package/dist/stablecoin.d.ts.map +1 -1
  60. package/dist/stablecoin.js +7 -0
  61. package/dist/stablecoin.js.map +1 -1
  62. package/dist/test.js +3 -0
  63. package/dist/test.js.map +1 -1
  64. package/dist/utils/compile-test.d.ts +2 -2
  65. package/dist/utils/compile-test.d.ts.map +1 -1
  66. package/dist/utils/compile-test.js +54 -3
  67. package/dist/utils/compile-test.js.map +1 -1
  68. package/dist/utils/version.d.ts +4 -4
  69. package/dist/utils/version.js +3 -3
  70. package/dist/zip-rust.d.ts.map +1 -1
  71. package/dist/zip-rust.js +3 -2
  72. package/dist/zip-rust.js.map +1 -1
  73. package/dist/zip-shared.d.ts +3 -4
  74. package/dist/zip-shared.d.ts.map +1 -1
  75. package/dist/zip-shared.js +20 -7
  76. package/dist/zip-shared.js.map +1 -1
  77. package/package.json +1 -1
  78. package/src/add-pausable.ts +0 -1
  79. package/src/add-upgradeable.ts +15 -16
  80. package/src/api.ts +15 -2
  81. package/src/build-generic.ts +6 -0
  82. package/src/contract.ts +6 -0
  83. package/src/fungible.ts +102 -40
  84. package/src/generate/fungible.ts +1 -0
  85. package/src/generate/governor.ts +24 -0
  86. package/src/generate/non-fungible.ts +1 -0
  87. package/src/generate/sources.ts +7 -0
  88. package/src/generate/stablecoin.ts +1 -0
  89. package/src/governor.ts +425 -0
  90. package/src/index.ts +2 -0
  91. package/src/kind.ts +1 -0
  92. package/src/non-fungible.ts +77 -14
  93. package/src/print.ts +6 -1
  94. package/src/set-access-control.ts +16 -10
  95. package/src/stablecoin.ts +8 -0
  96. package/src/test.ts +4 -0
  97. package/src/utils/compile-test.ts +84 -5
  98. package/src/utils/version.ts +3 -3
  99. package/src/zip-rust.ts +4 -2
  100. package/src/zip-shared.ts +17 -6
@@ -29,7 +29,7 @@ export function setAccessControl(c: ContractBuilder, access: Access, explicitImp
29
29
  const ownableTrait = {
30
30
  traitName: 'Ownable',
31
31
  structName: c.name,
32
- tags: explicitImplementations ? ['contractimpl'] : ['default_impl', 'contractimpl'],
32
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
33
33
  section: 'Utils',
34
34
  };
35
35
  if (explicitImplementations) {
@@ -51,7 +51,7 @@ export function setAccessControl(c: ContractBuilder, access: Access, explicitImp
51
51
  const accessControlTrait = {
52
52
  traitName: 'AccessControl',
53
53
  structName: c.name,
54
- tags: explicitImplementations ? ['contractimpl'] : ['default_impl', 'contractimpl'],
54
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
55
55
  section: 'Utils',
56
56
  };
57
57
  if (explicitImplementations) {
@@ -105,8 +105,9 @@ export function requireAccessControl(
105
105
 
106
106
  if (caller && role) {
107
107
  c.addUseClause('soroban_sdk', 'Symbol');
108
+ c.addUseClause('soroban_sdk', 'Vec');
108
109
  c.addConstructorArgument({ name: role, type: 'Address' });
109
- 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);`);
110
111
 
111
112
  if (useMacro) {
112
113
  c.addUseClause('stellar_macros', 'only_role');
@@ -114,7 +115,7 @@ export function requireAccessControl(
114
115
  } else {
115
116
  c.addFunctionCodeBefore(
116
117
  fn,
117
- [`access_control::ensure_role(e, ${caller}, &Symbol::new(e, "${role}"));`],
118
+ [`access_control::ensure_role(e, &Symbol::new(e, "${role}"), ${caller});`],
118
119
  trait,
119
120
  );
120
121
  }
@@ -162,6 +163,11 @@ const accessControlFunctions = defineFunctions({
162
163
  returns: 'Option<u32>',
163
164
  code: ['access_control::has_role(e, &account, &role)'],
164
165
  },
166
+ get_existing_roles: {
167
+ args: [getSelfArg()],
168
+ returns: 'Vec<Symbol>',
169
+ code: ['access_control::get_existing_roles(e)'],
170
+ },
165
171
  get_role_member_count: {
166
172
  args: [getSelfArg(), { name: 'role', type: 'Symbol' }],
167
173
  returns: 'u32',
@@ -185,24 +191,24 @@ const accessControlFunctions = defineFunctions({
185
191
  grant_role: {
186
192
  args: [
187
193
  getSelfArg(),
188
- { name: 'caller', type: 'Address' },
189
194
  { name: 'account', type: 'Address' },
190
195
  { name: 'role', type: 'Symbol' },
196
+ { name: 'caller', type: 'Address' },
191
197
  ],
192
- code: ['access_control::grant_role(e, &caller, &account, &role);'],
198
+ code: ['access_control::grant_role(e, &account, &role, &caller);'],
193
199
  },
194
200
  revoke_role: {
195
201
  args: [
196
202
  getSelfArg(),
197
- { name: 'caller', type: 'Address' },
198
203
  { name: 'account', type: 'Address' },
199
204
  { name: 'role', type: 'Symbol' },
205
+ { name: 'caller', type: 'Address' },
200
206
  ],
201
- code: ['access_control::revoke_role(e, &caller, &account, &role);'],
207
+ code: ['access_control::revoke_role(e, &account, &role, &caller);'],
202
208
  },
203
209
  renounce_role: {
204
- args: [getSelfArg(), { name: 'caller', type: 'Address' }, { name: 'role', type: 'Symbol' }],
205
- code: ['access_control::renounce_role(e, &caller, &role);'],
210
+ args: [getSelfArg(), { name: 'role', type: 'Symbol' }, { name: 'caller', type: 'Address' }],
211
+ code: ['access_control::renounce_role(e, &role, &caller);'],
206
212
  },
207
213
  transfer_admin_role: {
208
214
  args: [getSelfArg(), { name: 'new_admin', type: 'Address' }, { name: 'live_until_ledger', type: 'u32' }],
package/src/stablecoin.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  import { printContract } from './print';
12
12
  import { requireAccessControl, type Access } from './set-access-control';
13
13
  import { defineFunctions } from './utils/define-functions';
14
+ import { OptionsError } from './error';
14
15
 
15
16
  export const defaults: Required<StablecoinOptions> = {
16
17
  ...fungibleDefaults,
@@ -46,6 +47,13 @@ export function isAccessControlRequired(opts: Partial<StablecoinOptions>): boole
46
47
  export function buildStablecoin(opts: StablecoinOptions): Contract {
47
48
  const allOpts = withDefaults(opts);
48
49
 
50
+ if (allOpts.votes && allOpts.limitations) {
51
+ throw new OptionsError({
52
+ votes: 'Votes extension cannot be used with stablecoin limitations',
53
+ limitations: 'Stablecoin limitations cannot be used with Votes extension',
54
+ });
55
+ }
56
+
49
57
  const c = buildFungible(allOpts);
50
58
 
51
59
  if (allOpts.limitations) {
package/src/test.ts CHANGED
@@ -17,6 +17,10 @@ test.serial('fungible result generated', async t => {
17
17
  await testGenerate(t, 'Fungible');
18
18
  });
19
19
 
20
+ test.serial('governor result generated', async t => {
21
+ await testGenerate(t, 'Governor');
22
+ });
23
+
20
24
  test.serial('non-fungible result generated', async t => {
21
25
  await testGenerate(t, 'NonFungible');
22
26
  });
@@ -1,10 +1,12 @@
1
1
  import type { ExecutionContext } from 'ava';
2
2
  import { exec } from 'child_process';
3
- import { mkdir, mkdtemp, rm } from 'fs/promises';
4
- import { tmpdir } from 'os';
3
+ import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises';
4
+ import { homedir, tmpdir } from 'os';
5
5
  import path from 'path';
6
6
  import { promisify } from 'util';
7
7
  import type { GenericOptions } from '../build-generic';
8
+ import type { CommonContractOptions } from '../common-options';
9
+ import { withCommonContractDefaults } from '../common-options';
8
10
  import type { Contract } from '../contract';
9
11
  import { zipRustProject } from '../zip-rust';
10
12
  import { contractOptionsToContractName } from '../zip-shared';
@@ -18,6 +20,63 @@ export const runCargoTest = async (t: ExecutionContext, temporaryFolder: string)
18
20
  t.regex(result.stdout, /0 failed/);
19
21
  };
20
22
 
23
+ const resolvePath = (inputPath: string) =>
24
+ inputPath.startsWith('~/') ? path.join(homedir(), inputPath.slice(2)) : path.resolve(inputPath);
25
+ const toCargoPath = (inputPath: string) => inputPath.replace(/\\/g, '/');
26
+
27
+ const localDependencies = [
28
+ ['stellar-tokens', 'packages/tokens'],
29
+ ['stellar-access', 'packages/access'],
30
+ ['stellar-contract-utils', 'packages/contract-utils'],
31
+ ['stellar-governance', 'packages/governance'],
32
+ ['stellar-macros', 'packages/macros'],
33
+ ] as const;
34
+
35
+ const workspaceDependenciesPattern = /\[workspace\.dependencies\][\s\S]*?(?=\n\[profile\.release\])/;
36
+
37
+ async function rewriteWorkspaceCargoForLocalContracts(workspaceCargoPath: string, stellarContractsPath: string) {
38
+ const absoluteContractsPath = resolvePath(stellarContractsPath);
39
+ const upstreamCargoPath = path.join(absoluteContractsPath, 'Cargo.toml');
40
+ const upstreamLockPath = path.join(absoluteContractsPath, 'Cargo.lock');
41
+ const workspaceDir = path.dirname(workspaceCargoPath);
42
+
43
+ const [workspaceCargo, upstreamCargo] = await Promise.all([
44
+ readFile(workspaceCargoPath, 'utf8'),
45
+ readFile(upstreamCargoPath, 'utf8'),
46
+ ]);
47
+
48
+ const sorobanMatch = upstreamCargo.match(/^\s*soroban-sdk\s*=\s*"([^"]+)"/m);
49
+ if (sorobanMatch === null || sorobanMatch[1] === undefined) {
50
+ throw new Error(`Unable to find soroban-sdk version in ${upstreamCargoPath}`);
51
+ }
52
+
53
+ const dependencyLines = [
54
+ `soroban-sdk = "${sorobanMatch[1]}"`,
55
+ ...localDependencies.map(
56
+ ([crate, relativePath]) =>
57
+ `${crate} = { path = "${toCargoPath(path.join(absoluteContractsPath, relativePath))}" }`,
58
+ ),
59
+ ];
60
+
61
+ if (!workspaceDependenciesPattern.test(workspaceCargo)) {
62
+ throw new Error(`Unable to locate [workspace.dependencies] section in ${workspaceCargoPath}`);
63
+ }
64
+
65
+ const nextCargo = workspaceCargo.replace(
66
+ workspaceDependenciesPattern,
67
+ `[workspace.dependencies]\n${dependencyLines.join('\n')}\n`,
68
+ );
69
+
70
+ await writeFile(workspaceCargoPath, nextCargo);
71
+
72
+ // Reuse upstream lockfile when available to minimize resolver drift/offline cache misses.
73
+ try {
74
+ await copyFile(upstreamLockPath, path.join(workspaceDir, 'Cargo.lock'));
75
+ } catch {
76
+ // Ignore if lockfile is unavailable.
77
+ }
78
+ }
79
+
21
80
  type WithTemporaryFolderTestFunction<Args extends unknown[]> = (
22
81
  ...args: [...Args, ExecutionContext, string]
23
82
  ) => Promise<void> | void;
@@ -63,12 +122,21 @@ const doRunRustCompilationTest = async (
63
122
 
64
123
  assertLayout(test, zip, expandPathsFromFilesPaths(expectedZipFiles));
65
124
  await extractPackage(zip, folderPath);
125
+
126
+ if (process.env.STELLAR_CONTRACTS_PATH !== undefined) {
127
+ // Useful for release-candidate validation before crates.io publication.
128
+ await rewriteWorkspaceCargoForLocalContracts(
129
+ path.join(folderPath, 'Cargo.toml'),
130
+ process.env.STELLAR_CONTRACTS_PATH,
131
+ );
132
+ }
133
+
66
134
  await runCargoTest(test, folderPath);
67
135
 
68
136
  if (testOptions.snapshotResult) await snapshotZipContents(test, zip, expectedZipFiles);
69
137
  };
70
138
 
71
- export const runRustCompilationTest = withTemporaryFolderDo(
139
+ const _runRustCompilationTest = withTemporaryFolderDo(
72
140
  async (
73
141
  makeContract: MakeContract,
74
142
  opts: GenericOptions,
@@ -78,13 +146,16 @@ export const runRustCompilationTest = withTemporaryFolderDo(
78
146
  ) => {
79
147
  await doRunRustCompilationTest(makeContract, opts, testOptions, test, `${folderPath}/default`);
80
148
 
81
- const shouldBeExcludedOrHasAlreadyRun = testOptions.excludeExplicitTraitTest || opts.explicitImplementations;
149
+ const mergedOpts = withCommonContractDefaults(opts as GenericOptions & CommonContractOptions);
150
+ const supportsExplicitImplementations = 'explicitImplementations' in mergedOpts;
151
+ const shouldBeExcludedOrHasAlreadyRun =
152
+ testOptions.excludeExplicitTraitTest || !supportsExplicitImplementations || mergedOpts.explicitImplementations;
82
153
  if (shouldBeExcludedOrHasAlreadyRun) return;
83
154
 
84
155
  try {
85
156
  await doRunRustCompilationTest(
86
157
  makeContract,
87
- { ...opts, explicitImplementations: true },
158
+ { ...opts, explicitImplementations: true } as GenericOptions,
88
159
  testOptions,
89
160
  test,
90
161
  `${folderPath}/explicit`,
@@ -94,3 +165,11 @@ export const runRustCompilationTest = withTemporaryFolderDo(
94
165
  }
95
166
  },
96
167
  );
168
+
169
+ export function runRustCompilationTest<T extends GenericOptions>(
170
+ makeContract: (opt: T) => Contract,
171
+ opts: T,
172
+ testOptions: { snapshotResult: boolean; excludeExplicitTraitTest?: boolean },
173
+ ) {
174
+ return _runRustCompilationTest(makeContract as MakeContract, opts, testOptions);
175
+ }
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * The actual latest version to use in links.
3
3
  */
4
- export const contractsVersion = '0.4.1';
4
+ export const contractsVersion = '0.7.1';
5
5
  export const contractsVersionTag = `v${contractsVersion}`;
6
6
 
7
7
  /**
8
8
  * Semantic version string representing of the minimum compatible version of Contracts to display in output.
9
9
  */
10
- export const compatibleContractsSemver = '^0.4.1';
10
+ export const compatibleContractsSemver = '^0.7.1';
11
11
 
12
12
  /**
13
13
  * The Soroban version for which compilation and testing have passing tests
14
14
  */
15
- export const compatibleSorobanVersion = '22.0.8';
15
+ export const compatibleSorobanVersion = '25.3.0';
package/src/zip-rust.ts CHANGED
@@ -5,6 +5,7 @@ import { printContract, removeCreateLevelAttributes } from './print';
5
5
  import {
6
6
  contractOptionsToContractName,
7
7
  createRustLibFile,
8
+ getRequiredLibDependencies,
8
9
  printContractCargo,
9
10
  printRustNameTest,
10
11
  workspaceCargo,
@@ -33,12 +34,13 @@ export const createRustZipEnvironment = (c: Contract, opts: GenericOptions) => {
33
34
  const zip = new JSZip();
34
35
 
35
36
  const contractName = contractOptionsToContractName(opts?.kind || 'contract');
37
+ const requiredLibDeps = getRequiredLibDependencies(c);
36
38
 
37
39
  zip.file(`contracts/${contractName}/src/contract.rs`, removeCreateLevelAttributes(printContract(c)));
38
40
  zip.file(`contracts/${contractName}/src/test.rs`, printRustNameTest(c));
39
41
  zip.file(`contracts/${contractName}/src/lib.rs`, createRustLibFile);
40
- zip.file(`contracts/${contractName}/Cargo.toml`, printContractCargo(contractName));
41
- zip.file('Cargo.toml', workspaceCargo);
42
+ zip.file(`contracts/${contractName}/Cargo.toml`, printContractCargo(contractName, requiredLibDeps));
43
+ zip.file('Cargo.toml', workspaceCargo(requiredLibDeps));
42
44
  zip.file('.gitignore', gitIgnore);
43
45
 
44
46
  return zip;
package/src/zip-shared.ts CHANGED
@@ -39,19 +39,30 @@ fn initial_state() {
39
39
  // Add more tests bellow
40
40
  `;
41
41
 
42
- export const libDependencies = [
42
+ const libDependencies = [
43
43
  'stellar-tokens',
44
44
  'stellar-access',
45
45
  'stellar-contract-utils',
46
+ 'stellar-governance',
46
47
  'stellar-macros',
47
48
  ] as const;
48
49
 
49
- export const allDependencies = [...libDependencies, 'soroban-sdk'] as const;
50
+ // Derives required lib crate dependencies from the contract's use clauses.
51
+ // Each use clause has a containerPath like "stellar_tokens::fungible::Base".
52
+ // We extract the first segment (the crate name), convert underscores to hyphens
53
+ // to match Cargo naming (e.g., stellar_tokens -> stellar-tokens), then filter
54
+ // libDependencies to include only crates the contract actually references.
55
+ // Filtering against the known list preserves stable ordering and ignores
56
+ // non-lib crates like soroban_sdk (which is always added separately).
57
+ export function getRequiredLibDependencies(c: Pick<Contract, 'useClauses'>): string[] {
58
+ const usedCrates = new Set(c.useClauses.map(uc => uc.containerPath.split('::')[0]!.replace(/_/g, '-')));
59
+ return libDependencies.filter(dep => usedCrates.has(dep));
60
+ }
50
61
 
51
62
  export const addDependenciesWith = (dependencyValue: string, dependenciesToAdd: string[]) =>
52
63
  `${dependenciesToAdd.map(dependency => `${dependency} = ${dependencyValue}\n`).join('')}`;
53
64
 
54
- export const printContractCargo = (scaffoldContractName: string) => `[package]
65
+ export const printContractCargo = (scaffoldContractName: string, requiredLibDeps: readonly string[]) => `[package]
55
66
  name = "${scaffoldContractName.replace(/_/, '-')}-contract"
56
67
  edition.workspace = true
57
68
  license.workspace = true
@@ -66,7 +77,7 @@ crate-type = ["cdylib"]
66
77
  doctest = false
67
78
 
68
79
  [dependencies]
69
- ${addDependenciesWith('{ workspace = true }', [...allDependencies])}
80
+ ${addDependenciesWith('{ workspace = true }', [...requiredLibDeps, 'soroban-sdk'])}
70
81
  [dev-dependencies]
71
82
  ${addDependenciesWith('{ workspace = true, features = ["testutils"] }', ['soroban-sdk'])}`;
72
83
 
@@ -77,7 +88,7 @@ mod contract;
77
88
  mod test;
78
89
  `;
79
90
 
80
- export const workspaceCargo = `[workspace]
91
+ export const workspaceCargo = (requiredLibDeps: readonly string[]) => `[workspace]
81
92
  resolver = "2"
82
93
  members = ["contracts/*"]
83
94
 
@@ -88,7 +99,7 @@ license = "Apache-2.0"
88
99
  version = "0.0.1"
89
100
 
90
101
  [workspace.dependencies]
91
- ${addDependenciesWith(`"${compatibleSorobanVersion}"`, ['soroban-sdk'])}${addDependenciesWith(`"=${contractsVersion}"`, [...libDependencies])}
102
+ ${addDependenciesWith(`"${compatibleSorobanVersion}"`, ['soroban-sdk'])}${addDependenciesWith(`"=${contractsVersion}"`, [...requiredLibDeps])}
92
103
 
93
104
  [profile.release]
94
105
  opt-level = "z"