@openzeppelin/wizard-stellar 0.6.2 → 0.6.4

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 (70) hide show
  1. package/README.md +4 -0
  2. package/dist/account.d.ts +19 -0
  3. package/dist/account.d.ts.map +1 -0
  4. package/dist/account.js +251 -0
  5. package/dist/account.js.map +1 -0
  6. package/dist/add-upgradeable.d.ts +9 -0
  7. package/dist/add-upgradeable.d.ts.map +1 -1
  8. package/dist/add-upgradeable.js +25 -0
  9. package/dist/add-upgradeable.js.map +1 -1
  10. package/dist/api.d.ts +10 -0
  11. package/dist/api.d.ts.map +1 -1
  12. package/dist/api.js +12 -1
  13. package/dist/api.js.map +1 -1
  14. package/dist/build-generic.d.ts +8 -0
  15. package/dist/build-generic.d.ts.map +1 -1
  16. package/dist/build-generic.js +6 -0
  17. package/dist/build-generic.js.map +1 -1
  18. package/dist/contract.d.ts +21 -2
  19. package/dist/contract.d.ts.map +1 -1
  20. package/dist/contract.js +11 -0
  21. package/dist/contract.js.map +1 -1
  22. package/dist/generate/account.d.ts +3 -0
  23. package/dist/generate/account.d.ts.map +1 -0
  24. package/dist/generate/account.js +21 -0
  25. package/dist/generate/account.js.map +1 -0
  26. package/dist/generate/sources.d.ts.map +1 -1
  27. package/dist/generate/sources.js +12 -0
  28. package/dist/generate/sources.js.map +1 -1
  29. package/dist/generate/vault.d.ts +3 -0
  30. package/dist/generate/vault.d.ts.map +1 -0
  31. package/dist/generate/vault.js +21 -0
  32. package/dist/generate/vault.js.map +1 -0
  33. package/dist/index.d.ts +4 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +9 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/kind.js +2 -0
  38. package/dist/kind.js.map +1 -1
  39. package/dist/print.js +39 -9
  40. package/dist/print.js.map +1 -1
  41. package/dist/utils/compile-test.d.ts.map +1 -1
  42. package/dist/utils/compile-test.js +5 -1
  43. package/dist/utils/compile-test.js.map +1 -1
  44. package/dist/vault.d.ts +16 -0
  45. package/dist/vault.d.ts.map +1 -0
  46. package/dist/vault.js +334 -0
  47. package/dist/vault.js.map +1 -0
  48. package/dist/zip-rust.d.ts.map +1 -1
  49. package/dist/zip-rust.js +14 -1
  50. package/dist/zip-rust.js.map +1 -1
  51. package/dist/zip-shared.d.ts +15 -0
  52. package/dist/zip-shared.d.ts.map +1 -1
  53. package/dist/zip-shared.js +271 -1
  54. package/dist/zip-shared.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/account.ts +319 -0
  57. package/src/add-upgradeable.ts +29 -0
  58. package/src/api.ts +25 -0
  59. package/src/build-generic.ts +12 -0
  60. package/src/contract.ts +24 -2
  61. package/src/generate/account.ts +21 -0
  62. package/src/generate/sources.ts +14 -0
  63. package/src/generate/vault.ts +21 -0
  64. package/src/index.ts +4 -0
  65. package/src/kind.ts +2 -0
  66. package/src/print.ts +47 -18
  67. package/src/utils/compile-test.ts +6 -1
  68. package/src/vault.ts +378 -0
  69. package/src/zip-rust.ts +17 -1
  70. package/src/zip-shared.ts +293 -0
package/src/api.ts CHANGED
@@ -1,13 +1,20 @@
1
1
  import type { CommonContractOptions, CommonOptions } from './common-options';
2
+ import type { AccountOptions } from './account';
2
3
  import type { FungibleOptions } from './fungible';
3
4
  import type { GovernorOptions } from './governor';
4
5
  import type { NonFungibleOptions } from './non-fungible';
5
6
  import type { StablecoinOptions } from './stablecoin';
7
+ import type { VaultOptions } from './vault';
6
8
  import {
7
9
  printStablecoin,
8
10
  defaults as stablecoinDefaults,
9
11
  isAccessControlRequired as stablecoinIsAccessControlRequired,
10
12
  } from './stablecoin';
13
+ import {
14
+ printVault,
15
+ defaults as vaultDefaults,
16
+ isAccessControlRequired as vaultIsAccessControlRequired,
17
+ } from './vault';
11
18
  import {
12
19
  printFungible,
13
20
  defaults as fungibledefaults,
@@ -23,6 +30,7 @@ import {
23
30
  defaults as nonFungibledefaults,
24
31
  isAccessControlRequired as nonFungibleIsAccessControlRequired,
25
32
  } from './non-fungible';
33
+ import { printAccount, defaults as accountDefaults } from './account';
26
34
 
27
35
  export interface WizardContractAPI<Options extends CommonOptions> {
28
36
  /**
@@ -44,10 +52,21 @@ export interface AccessControlAPI<Options extends CommonContractOptions> {
44
52
  isAccessControlRequired: (opts: Partial<Options>) => boolean;
45
53
  }
46
54
 
55
+ /**
56
+ * A smart account authorizes everything through `__check_auth` and its context
57
+ * rules, so it has no `access` option and therefore no `AccessControlAPI`.
58
+ */
59
+ export type Account = WizardContractAPI<AccountOptions>;
47
60
  export type Fungible = WizardContractAPI<FungibleOptions> & AccessControlAPI<FungibleOptions>;
48
61
  export type Governor = WizardContractAPI<GovernorOptions> & AccessControlAPI<GovernorOptions>;
49
62
  export type NonFungible = WizardContractAPI<NonFungibleOptions> & AccessControlAPI<NonFungibleOptions>;
50
63
  export type Stablecoin = WizardContractAPI<StablecoinOptions> & AccessControlAPI<StablecoinOptions>;
64
+ export type Vault = WizardContractAPI<VaultOptions> & AccessControlAPI<VaultOptions>;
65
+
66
+ export const account: Account = {
67
+ print: printAccount,
68
+ defaults: accountDefaults,
69
+ };
51
70
 
52
71
  export const fungible: Fungible = {
53
72
  print: printFungible,
@@ -72,3 +91,9 @@ export const stablecoin: Stablecoin = {
72
91
  defaults: stablecoinDefaults,
73
92
  isAccessControlRequired: stablecoinIsAccessControlRequired,
74
93
  };
94
+
95
+ export const vault: Vault = {
96
+ print: printVault,
97
+ defaults: vaultDefaults,
98
+ isAccessControlRequired: vaultIsAccessControlRequired,
99
+ };
@@ -1,3 +1,5 @@
1
+ import type { AccountOptions } from './account';
2
+ import { buildAccount } from './account';
1
3
  import type { FungibleOptions } from './fungible';
2
4
  import { buildFungible } from './fungible';
3
5
  import type { GovernorOptions } from './governor';
@@ -6,18 +8,25 @@ import type { NonFungibleOptions } from './non-fungible';
6
8
  import { buildNonFungible } from './non-fungible';
7
9
  import type { StablecoinOptions } from './stablecoin';
8
10
  import { buildStablecoin } from './stablecoin';
11
+ import type { VaultOptions } from './vault';
12
+ import { buildVault } from './vault';
9
13
 
10
14
  export interface KindedOptions {
15
+ Account: { kind: 'Account' } & AccountOptions;
11
16
  Fungible: { kind: 'Fungible' } & FungibleOptions;
12
17
  Governor: { kind: 'Governor' } & GovernorOptions;
13
18
  NonFungible: { kind: 'NonFungible' } & NonFungibleOptions;
14
19
  Stablecoin: { kind: 'Stablecoin' } & StablecoinOptions;
20
+ Vault: { kind: 'Vault' } & VaultOptions;
15
21
  }
16
22
 
17
23
  export type GenericOptions = KindedOptions[keyof KindedOptions];
18
24
 
19
25
  export function buildGeneric(opts: GenericOptions) {
20
26
  switch (opts.kind) {
27
+ case 'Account':
28
+ return buildAccount(opts);
29
+
21
30
  case 'Fungible':
22
31
  return buildFungible(opts);
23
32
 
@@ -30,6 +39,9 @@ export function buildGeneric(opts: GenericOptions) {
30
39
  case 'Stablecoin':
31
40
  return buildStablecoin(opts);
32
41
 
42
+ case 'Vault':
43
+ return buildVault(opts);
44
+
33
45
  default: {
34
46
  const _: never = opts;
35
47
  throw new Error('Unknown kind');
package/src/contract.ts CHANGED
@@ -35,7 +35,11 @@ export interface BaseTraitImplBlock {
35
35
  traitName: string;
36
36
  structName: string;
37
37
  tags: string[];
38
- assocType?: string;
38
+ /**
39
+ * Associated type bindings for the impl block. An array declares several of
40
+ * them, one per line (e.g. `CustomAccountInterface`'s `Error` and `Signature`).
41
+ */
42
+ assocType?: string | string[];
39
43
  section?: string;
40
44
  /**
41
45
  * Priority for which trait to print first.
@@ -70,6 +74,12 @@ export interface Variable {
70
74
  export interface Argument {
71
75
  name: string;
72
76
  type?: string;
77
+ /**
78
+ * Comment lines printed above the argument. Forces one argument per line, so
79
+ * only worth using where the caller needs guidance that the type cannot carry,
80
+ * such as where to obtain an address.
81
+ */
82
+ comment?: string[];
73
83
  }
74
84
 
75
85
  export class ContractBuilder implements Contract {
@@ -215,7 +225,7 @@ export class ContractBuilder implements Contract {
215
225
  return contractFn;
216
226
  }
217
227
 
218
- overrideAssocType(traitName: string, newAssocType: string): void {
228
+ overrideAssocType(traitName: string, newAssocType: string | string[]): void {
219
229
  const trait = this.implementedTraitsMap.get(traitName);
220
230
  if (trait) {
221
231
  trait.assocType = newAssocType;
@@ -274,6 +284,18 @@ export class ContractBuilder implements Contract {
274
284
  this.constructorCode.push(code);
275
285
  }
276
286
 
287
+ /**
288
+ * Appends a block of constructor code verbatim, keeping duplicate lines.
289
+ *
290
+ * `addConstructorCode` deduplicates, which is right for the idempotent setup
291
+ * statements each generator contributes, but wrong for a multi-line block
292
+ * where lines such as `}` legitimately repeat. Empty entries separate groups
293
+ * and are printed as blank lines.
294
+ */
295
+ addConstructorCodeBlock(lines: string[]): void {
296
+ this.constructorCode.push(...lines);
297
+ }
298
+
277
299
  addDerives(derive: string): void {
278
300
  this.derivesSet.add(derive);
279
301
  }
@@ -0,0 +1,21 @@
1
+ import type { AccountOptions } from '../account';
2
+ import { policyOptions } from '../account';
3
+ import { infoOptions } from '../set-info';
4
+ import { generateAlternatives } from './alternatives';
5
+
6
+ const booleans = [true, false];
7
+
8
+ const blueprint = {
9
+ name: ['MyAccount'],
10
+ delegatedSigners: booleans,
11
+ ed25519Signers: booleans,
12
+ webauthnSigners: booleans,
13
+ policy: policyOptions,
14
+ executionEntryPoint: booleans,
15
+ upgradeable: booleans,
16
+ info: infoOptions,
17
+ };
18
+
19
+ export function* generateAccountOptions(): Generator<Required<AccountOptions>> {
20
+ yield* generateAlternatives(blueprint);
21
+ }
@@ -2,10 +2,12 @@ import { promises as fs } from 'fs';
2
2
  import path from 'path';
3
3
  import crypto from 'crypto';
4
4
 
5
+ import { generateAccountOptions } from './account';
5
6
  import { generateFungibleOptions } from './fungible';
6
7
  import { generateGovernorOptions } from './governor';
7
8
  import { generateNonFungibleOptions } from './non-fungible';
8
9
  import { generateStablecoinOptions } from './stablecoin';
10
+ import { generateVaultOptions } from './vault';
9
11
  import type { GenericOptions, KindedOptions } from '../build-generic';
10
12
  import { buildGeneric } from '../build-generic';
11
13
  import { printContract } from '../print';
@@ -15,6 +17,12 @@ import type { Contract } from '../contract';
15
17
  type Kind = keyof KindedOptions;
16
18
 
17
19
  export function* generateOptions(kind?: Kind): Generator<GenericOptions> {
20
+ if (!kind || kind === 'Account') {
21
+ for (const kindOpts of generateAccountOptions()) {
22
+ yield { kind: 'Account', ...kindOpts };
23
+ }
24
+ }
25
+
18
26
  if (!kind || kind === 'Fungible') {
19
27
  for (const kindOpts of generateFungibleOptions()) {
20
28
  yield { kind: 'Fungible', ...kindOpts };
@@ -38,6 +46,12 @@ export function* generateOptions(kind?: Kind): Generator<GenericOptions> {
38
46
  yield { kind: 'Stablecoin', ...kindOpts };
39
47
  }
40
48
  }
49
+
50
+ if (!kind || kind === 'Vault') {
51
+ for (const kindOpts of generateVaultOptions()) {
52
+ yield { kind: 'Vault', ...kindOpts };
53
+ }
54
+ }
41
55
  }
42
56
 
43
57
  interface GeneratedContract {
@@ -0,0 +1,21 @@
1
+ import type { VaultOptions } from '../vault';
2
+ import { accessOptions } from '../set-access-control';
3
+ import { infoOptions } from '../set-info';
4
+ import { generateAlternatives } from './alternatives';
5
+
6
+ const booleans = [true, false];
7
+
8
+ const blueprint = {
9
+ name: ['MyVault'],
10
+ symbol: ['MTK'],
11
+ decimalsOffset: ['0', '2'],
12
+ pausable: booleans,
13
+ upgradeable: booleans,
14
+ access: accessOptions,
15
+ info: infoOptions,
16
+ explicitImplementations: booleans,
17
+ };
18
+
19
+ export function* generateVaultOptions(): Generator<Required<VaultOptions>> {
20
+ yield* generateAlternatives(blueprint);
21
+ }
package/src/index.ts CHANGED
@@ -21,12 +21,16 @@ export { sanitizeKind } from './kind';
21
21
 
22
22
  export { contractsVersion, contractsVersionTag, compatibleContractsSemver } from './utils/version';
23
23
 
24
+ export { account } from './api';
24
25
  export { fungible } from './api';
25
26
  export { governor } from './api';
26
27
  export { nonFungible } from './api';
27
28
  export { stablecoin } from './api';
29
+ export { vault } from './api';
28
30
 
31
+ export type { AccountOptions, Policy } from './account';
29
32
  export type { FungibleOptions } from './fungible';
30
33
  export type { GovernorOptions } from './governor';
31
34
  export type { NonFungibleOptions } from './non-fungible';
32
35
  export type { StablecoinOptions } from './stablecoin';
36
+ export type { VaultOptions } from './vault';
package/src/kind.ts CHANGED
@@ -14,10 +14,12 @@ export function sanitizeKind(kind: unknown): Kind {
14
14
 
15
15
  function isKind<T>(value: Kind | T): value is Kind {
16
16
  switch (value) {
17
+ case 'Account':
17
18
  case 'Fungible':
18
19
  case 'Governor':
19
20
  case 'NonFungible':
20
21
  case 'Stablecoin':
22
+ case 'Vault':
21
23
  return true;
22
24
 
23
25
  default: {
package/src/print.ts CHANGED
@@ -64,8 +64,17 @@ function printContractErrors(contract: Contract): Lines[] {
64
64
  ];
65
65
  }
66
66
 
67
+ // Lines that open, close or continue a block or expression are not statements,
68
+ // so they must not be terminated. Needed by multi-line code such as a `for` loop
69
+ // or a call whose arguments are spread over several lines.
70
+ const CONTINUATION_LINE_ENDINGS = ['{', '}', ';', ',', '('];
71
+
72
+ function needsSemicolon(line: string): boolean {
73
+ return line.length > 0 && !CONTINUATION_LINE_ENDINGS.includes(line.at(-1)!);
74
+ }
75
+
67
76
  function withSemicolons(lines: string[]): string[] {
68
- return lines.map(line => (line.endsWith(';') ? line : line + ';'));
77
+ return lines.map(line => (needsSemicolon(line) ? line + ';' : line));
69
78
  }
70
79
 
71
80
  function printVariables(contract: Contract): string[] {
@@ -208,8 +217,11 @@ function printImplementedTrait(trait: TraitImplBlock): Lines[] {
208
217
 
209
218
  const head = `impl ${trait.traitName} for ${trait.structName}`;
210
219
 
211
- const assocTypeLines =
212
- trait.assocType !== undefined && trait.assocType.trim().length > 0 ? [TAB + trait.assocType.trim(), ''] : [];
220
+ const assocTypes = (Array.isArray(trait.assocType) ? trait.assocType : [trait.assocType ?? ''])
221
+ .map(assocType => assocType.trim())
222
+ .filter(assocType => assocType.length > 0);
223
+
224
+ const assocTypeLines = assocTypes.length > 0 ? [...assocTypes.map(assocType => TAB + assocType), ''] : [];
213
225
 
214
226
  const functionLines = trait.functions.map(fn => printFunction(fn));
215
227
 
@@ -234,14 +246,14 @@ function printImplementedTrait(trait: TraitImplBlock): Lines[] {
234
246
 
235
247
  function printFunction(fn: ContractFunction): Lines[] {
236
248
  const head = `fn ${fn.name}`;
237
- const args = fn.args.map(a => printArgument(a));
249
+ const args = printArguments(fn.args);
238
250
 
239
251
  const codeLines = fn.codeBefore?.concat(fn.code) ?? fn.code;
240
252
  for (let i = 0; i < codeLines.length; i++) {
241
253
  const line = codeLines[i];
242
254
  const shouldEndWithSemicolon = i < codeLines.length - 1 || fn.returns === undefined;
243
255
  if (line !== undefined && line.length > 0) {
244
- if (shouldEndWithSemicolon && !['{', '}', ';'].includes(line.charAt(line.length - 1))) {
256
+ if (shouldEndWithSemicolon && needsSemicolon(line)) {
245
257
  codeLines[i] += ';';
246
258
  } else if (!shouldEndWithSemicolon && line.endsWith(';')) {
247
259
  codeLines[i] = line.slice(0, line.length - 1);
@@ -284,17 +296,23 @@ function printConstructor(contract: Contract): Lines[] {
284
296
  const head = 'fn __constructor';
285
297
  const args = [getSelfArg(), ...contract.constructorArgs];
286
298
 
287
- const body = spaceBetween(withSemicolons(contract.constructorCode));
288
-
289
- const constructor = printFunction2(
290
- true,
291
- head,
292
- args.map(a => printArgument(a)),
293
- [],
294
- undefined,
295
- undefined,
296
- body,
299
+ // Empty entries separate logical groups of statements. Routing them through
300
+ // `spaceBetween` yields blank lines with no trailing indentation.
301
+ const groups = contract.constructorCode.reduce<string[][]>(
302
+ (acc, line) => {
303
+ if (line.length === 0) {
304
+ acc.push([]);
305
+ } else {
306
+ acc[acc.length - 1]!.push(line);
307
+ }
308
+ return acc;
309
+ },
310
+ [[]],
297
311
  );
312
+
313
+ const body = spaceBetween(...groups.map(group => withSemicolons(group)));
314
+
315
+ const constructor = printFunction2(true, head, printArguments(args), [], undefined, undefined, body);
298
316
  return constructor;
299
317
  } else {
300
318
  return [];
@@ -328,11 +346,12 @@ function printFunction2(
328
346
 
329
347
  if (args.length > 0) {
330
348
  const formattedArgs = args.join(', ');
331
- if (formattedArgs.length > 80) {
349
+ // Commented arguments cannot share a line, so they force the expanded form.
350
+ if (formattedArgs.length > 80 || args.some(isComment)) {
332
351
  fn.push(accum);
333
352
  accum = '';
334
353
  // print each arg in a separate line
335
- fn.push(args.map(arg => `${arg},`));
354
+ fn.push(args.map(arg => (isComment(arg) ? arg : `${arg},`)));
336
355
  } else {
337
356
  accum += `${formattedArgs}`;
338
357
  }
@@ -364,13 +383,23 @@ function printArgument(arg: Argument): string {
364
383
  }
365
384
  }
366
385
 
386
+ // Expands each argument into its comment lines followed by the argument itself.
387
+ // `printFunction2` recognizes the comment entries and leaves them untouched.
388
+ function printArguments(args: Argument[]): string[] {
389
+ return args.flatMap(arg => [...(arg.comment ?? []).map(line => `// ${line}`), printArgument(arg)]);
390
+ }
391
+
392
+ function isComment(line: string): boolean {
393
+ return line.startsWith('//');
394
+ }
395
+
367
396
  function printTopLevelDeclarations(contract: Contract): Lines[] {
368
397
  if (contract.topLevelDeclarations.length === 0) return [];
369
398
  return spaceBetween(...contract.topLevelDeclarations);
370
399
  }
371
400
 
372
401
  function printDocumentations(documentations: string[]): string[] {
373
- return documentations.map(documentation => `//! ${documentation}`);
402
+ return documentations.map(documentation => (documentation.length > 0 ? `//! ${documentation}` : '//!'));
374
403
  }
375
404
 
376
405
  function printMetadata(contract: Contract) {
@@ -27,6 +27,7 @@ const toCargoPath = (inputPath: string) => inputPath.replace(/\\/g, '/');
27
27
  const localDependencies = [
28
28
  ['stellar-tokens', 'packages/tokens'],
29
29
  ['stellar-access', 'packages/access'],
30
+ ['stellar-accounts', 'packages/accounts'],
30
31
  ['stellar-contract-utils', 'packages/contract-utils'],
31
32
  ['stellar-governance', 'packages/governance'],
32
33
  ['stellar-macros', 'packages/macros'],
@@ -45,7 +46,11 @@ async function rewriteWorkspaceCargoForLocalContracts(workspaceCargoPath: string
45
46
  readFile(upstreamCargoPath, 'utf8'),
46
47
  ]);
47
48
 
48
- const sorobanMatch = upstreamCargo.match(/^\s*soroban-sdk\s*=\s*"([^"]+)"/m);
49
+ // Match both the bare-string form (`soroban-sdk = "26.1.0"`) and the table
50
+ // form (`soroban-sdk = { version = "26.1.0", features = [...] }`).
51
+ const sorobanMatch =
52
+ upstreamCargo.match(/^\s*soroban-sdk\s*=\s*"([^"]+)"/m) ??
53
+ upstreamCargo.match(/^\s*soroban-sdk\s*=\s*\{[^}]*?version\s*=\s*"([^"]+)"/m);
49
54
  if (sorobanMatch === null || sorobanMatch[1] === undefined) {
50
55
  throw new Error(`Unable to find soroban-sdk version in ${upstreamCargoPath}`);
51
56
  }