@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/vault.ts ADDED
@@ -0,0 +1,378 @@
1
+ import { ContractBuilder } from './contract';
2
+ import type { Access } from './set-access-control';
3
+ import { setAccessControl } from './set-access-control';
4
+ import { addPausable } from './add-pausable';
5
+ import { addUpgradeable } from './add-upgradeable';
6
+ import { defineFunctions } from './utils/define-functions';
7
+ import type { CommonContractOptions } from './common-options';
8
+ import { withCommonContractDefaults, getSelfArg } from './common-options';
9
+ import { setInfo } from './set-info';
10
+ import { contractDefaults as commonDefaults } from './common-options';
11
+ import { printContract } from './print';
12
+ import { toByteArray, toUint } from './utils/convert-strings';
13
+ import { OptionsError } from './error';
14
+ import { pickKeys } from '@openzeppelin/wizard-common';
15
+
16
+ // Upper bound enforced by the library's `Vault::set_decimals_offset`
17
+ // (`stellar_tokens::vault::MAX_DECIMALS_OFFSET`); a larger value panics on deploy.
18
+ const MAX_DECIMALS_OFFSET = 10;
19
+
20
+ export const defaults: Required<VaultOptions> = {
21
+ name: 'MyVault',
22
+ symbol: 'MTK',
23
+ decimalsOffset: '0',
24
+ pausable: false,
25
+ upgradeable: false,
26
+ access: commonDefaults.access,
27
+ info: commonDefaults.info,
28
+ explicitImplementations: commonDefaults.explicitImplementations,
29
+ } as const;
30
+
31
+ export function printVault(opts: VaultOptions = defaults): string {
32
+ return printContract(buildVault(opts));
33
+ }
34
+
35
+ export interface VaultOptions extends CommonContractOptions {
36
+ name: string;
37
+ symbol: string;
38
+ decimalsOffset?: string;
39
+ pausable?: boolean;
40
+ upgradeable?: boolean;
41
+ }
42
+
43
+ export function withDefaults(opts: VaultOptions): Required<VaultOptions> {
44
+ return {
45
+ ...opts,
46
+ ...withCommonContractDefaults(opts),
47
+ decimalsOffset: opts.decimalsOffset ?? defaults.decimalsOffset,
48
+ pausable: opts.pausable ?? defaults.pausable,
49
+ upgradeable: opts.upgradeable ?? defaults.upgradeable,
50
+ };
51
+ }
52
+
53
+ export function isAccessControlRequired(opts: Partial<VaultOptions>): boolean {
54
+ return opts.pausable === true || opts.upgradeable === true;
55
+ }
56
+
57
+ export function buildVault(opts: VaultOptions): ContractBuilder {
58
+ const c = new ContractBuilder(opts.name);
59
+
60
+ const allOpts = withDefaults(opts);
61
+
62
+ const decimalsOffset = toUint(allOpts.decimalsOffset, 'decimalsOffset', 'u32');
63
+ if (decimalsOffset > MAX_DECIMALS_OFFSET) {
64
+ throw new OptionsError({
65
+ decimalsOffset: `Maximum decimals offset is ${MAX_DECIMALS_OFFSET}`,
66
+ });
67
+ }
68
+
69
+ addBase(
70
+ c,
71
+ toByteArray(allOpts.name),
72
+ toByteArray(allOpts.symbol),
73
+ decimalsOffset,
74
+ allOpts.pausable,
75
+ allOpts.explicitImplementations,
76
+ );
77
+
78
+ if (allOpts.pausable) {
79
+ addPausable(c, allOpts.access, allOpts.explicitImplementations);
80
+ }
81
+
82
+ if (allOpts.upgradeable) {
83
+ addUpgradeable(c, allOpts.access, allOpts.explicitImplementations);
84
+ }
85
+
86
+ setAccessControl(c, allOpts.access, allOpts.explicitImplementations);
87
+
88
+ // The AccessControl trait surface (e.g. `get_existing_roles`, role lookups)
89
+ // references `Symbol` and `Vec`, which the contracttrait macro and explicit
90
+ // implementations need in scope even when no function is role-gated.
91
+ if (allOpts.access === 'roles') {
92
+ c.addUseClause('soroban_sdk', 'Symbol');
93
+ c.addUseClause('soroban_sdk', 'Vec');
94
+ }
95
+
96
+ setInfo(c, allOpts.info);
97
+
98
+ return c;
99
+ }
100
+
101
+ function addBase(
102
+ c: ContractBuilder,
103
+ name: string,
104
+ symbol: string,
105
+ decimalsOffset: bigint,
106
+ pausable: boolean,
107
+ explicitImplementations: boolean,
108
+ ) {
109
+ // The underlying asset is supplied at deployment, while the virtual decimals
110
+ // offset is a baked-in configuration value. The offset must be set before the
111
+ // metadata because the vault derives its decimals from the underlying asset's
112
+ // decimals plus the offset.
113
+ c.addConstructorArgument({ name: 'asset', type: 'Address' });
114
+ c.addConstructorCode('Vault::set_asset(e, asset);');
115
+ c.addConstructorCode(`Vault::set_decimals_offset(e, ${decimalsOffset});`);
116
+ c.addConstructorCode(
117
+ `Base::set_metadata(e, Self::decimals(e), String::from_str(e, "${name}"), String::from_str(e, "${symbol}"));`,
118
+ );
119
+
120
+ // Set token functions
121
+ c.addUseClause('stellar_tokens::fungible', 'Base');
122
+ c.addUseClause('stellar_tokens::fungible', 'FungibleToken');
123
+ c.addUseClause('stellar_tokens::vault', 'FungibleVault');
124
+ c.addUseClause('stellar_tokens::vault', 'Vault');
125
+ // The base token functions we emit delegate to `Self::ContractType::...`,
126
+ // which resolve through the `ContractOverrides` trait that `Vault` implements,
127
+ // so it must be in scope. This happens with explicit implementations (all
128
+ // functions are spelled out) and when pausable adds transfer overrides.
129
+ if (explicitImplementations || pausable) c.addUseClause('stellar_tokens::fungible', 'ContractOverrides');
130
+ c.addUseClause('soroban_sdk', 'contract');
131
+ c.addUseClause('soroban_sdk', 'contractimpl');
132
+ c.addUseClause('soroban_sdk', 'String');
133
+ c.addUseClause('soroban_sdk', 'Env');
134
+ c.addUseClause('soroban_sdk', 'Address');
135
+ c.addUseClause('soroban_sdk', 'MuxedAddress');
136
+
137
+ const fungibleTokenTrait = {
138
+ traitName: 'FungibleToken',
139
+ structName: c.name,
140
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
141
+ assocType: 'type ContractType = Vault;',
142
+ };
143
+
144
+ c.addTraitImplBlock(fungibleTokenTrait);
145
+
146
+ if (explicitImplementations) {
147
+ c.addTraitForEachFunctions(fungibleTokenTrait, fungibleTokenTraitFunctions);
148
+ } else {
149
+ // The vault overrides decimals to derive them from the underlying asset and
150
+ // the virtual decimals offset.
151
+ c.addTraitFunction(fungibleTokenTrait, functions.decimals);
152
+ }
153
+
154
+ if (pausable) {
155
+ c.addUseClause('stellar_macros', 'when_not_paused');
156
+
157
+ c.addTraitFunction(fungibleTokenTrait, functions.transfer);
158
+ c.addFunctionTag(functions.transfer, 'when_not_paused', fungibleTokenTrait);
159
+
160
+ c.addTraitFunction(fungibleTokenTrait, functions.transfer_from);
161
+ c.addFunctionTag(functions.transfer_from, 'when_not_paused', fungibleTokenTrait);
162
+ }
163
+
164
+ const fungibleVaultTrait = {
165
+ traitName: 'FungibleVault',
166
+ structName: c.name,
167
+ tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
168
+ section: 'Extensions',
169
+ };
170
+
171
+ if (explicitImplementations) {
172
+ c.addTraitForEachFunctions(fungibleVaultTrait, fungibleVaultTraitFunctions);
173
+ } else {
174
+ c.addTraitImplBlock(fungibleVaultTrait);
175
+ }
176
+ }
177
+
178
+ export const functions = defineFunctions({
179
+ // FungibleToken Trait
180
+ total_supply: {
181
+ args: [getSelfArg()],
182
+ returns: 'i128',
183
+ code: ['Self::ContractType::total_supply(e)'],
184
+ },
185
+ balance: {
186
+ args: [getSelfArg(), { name: 'account', type: 'Address' }],
187
+ returns: 'i128',
188
+ code: ['Self::ContractType::balance(e, &account)'],
189
+ },
190
+ allowance: {
191
+ args: [getSelfArg(), { name: 'owner', type: 'Address' }, { name: 'spender', type: 'Address' }],
192
+ returns: 'i128',
193
+ code: ['Self::ContractType::allowance(e, &owner, &spender)'],
194
+ },
195
+ transfer: {
196
+ args: [
197
+ getSelfArg(),
198
+ { name: 'from', type: 'Address' },
199
+ { name: 'to', type: 'MuxedAddress' },
200
+ { name: 'amount', type: 'i128' },
201
+ ],
202
+ code: ['Self::ContractType::transfer(e, &from, &to, amount)'],
203
+ },
204
+ transfer_from: {
205
+ args: [
206
+ getSelfArg(),
207
+ { name: 'spender', type: 'Address' },
208
+ { name: 'from', type: 'Address' },
209
+ { name: 'to', type: 'Address' },
210
+ { name: 'amount', type: 'i128' },
211
+ ],
212
+ code: ['Self::ContractType::transfer_from(e, &spender, &from, &to, amount)'],
213
+ },
214
+ approve: {
215
+ args: [
216
+ getSelfArg(),
217
+ { name: 'owner', type: 'Address' },
218
+ { name: 'spender', type: 'Address' },
219
+ { name: 'amount', type: 'i128' },
220
+ { name: 'live_until_ledger', type: 'u32' },
221
+ ],
222
+ code: ['Self::ContractType::approve(e, &owner, &spender, amount, live_until_ledger)'],
223
+ },
224
+ // The vault derives decimals from the underlying asset's decimals plus the
225
+ // virtual offset, so it overrides the default metadata-based implementation.
226
+ decimals: {
227
+ args: [getSelfArg()],
228
+ returns: 'u32',
229
+ code: ['Vault::decimals(e)'],
230
+ },
231
+ name: {
232
+ args: [getSelfArg()],
233
+ returns: 'String',
234
+ code: ['Self::ContractType::name(e)'],
235
+ },
236
+ symbol: {
237
+ args: [getSelfArg()],
238
+ returns: 'String',
239
+ code: ['Self::ContractType::symbol(e)'],
240
+ },
241
+
242
+ // FungibleVault Trait
243
+ query_asset: {
244
+ args: [getSelfArg()],
245
+ returns: 'Address',
246
+ code: ['Self::ContractType::query_asset(e)'],
247
+ },
248
+ total_assets: {
249
+ args: [getSelfArg()],
250
+ returns: 'i128',
251
+ code: ['Self::ContractType::total_assets(e)'],
252
+ },
253
+ convert_to_shares: {
254
+ args: [getSelfArg(), { name: 'assets', type: 'i128' }],
255
+ returns: 'i128',
256
+ code: ['Self::ContractType::convert_to_shares(e, assets)'],
257
+ },
258
+ convert_to_assets: {
259
+ args: [getSelfArg(), { name: 'shares', type: 'i128' }],
260
+ returns: 'i128',
261
+ code: ['Self::ContractType::convert_to_assets(e, shares)'],
262
+ },
263
+ max_deposit: {
264
+ args: [getSelfArg(), { name: 'receiver', type: 'Address' }],
265
+ returns: 'i128',
266
+ code: ['Self::ContractType::max_deposit(e, receiver)'],
267
+ },
268
+ preview_deposit: {
269
+ args: [getSelfArg(), { name: 'assets', type: 'i128' }],
270
+ returns: 'i128',
271
+ code: ['Self::ContractType::preview_deposit(e, assets)'],
272
+ },
273
+ deposit: {
274
+ args: [
275
+ getSelfArg(),
276
+ { name: 'assets', type: 'i128' },
277
+ { name: 'receiver', type: 'Address' },
278
+ { name: 'from', type: 'Address' },
279
+ { name: 'operator', type: 'Address' },
280
+ ],
281
+ returns: 'i128',
282
+ code: ['Self::ContractType::deposit(e, assets, receiver, from, operator)'],
283
+ },
284
+ max_mint: {
285
+ args: [getSelfArg(), { name: 'receiver', type: 'Address' }],
286
+ returns: 'i128',
287
+ code: ['Self::ContractType::max_mint(e, receiver)'],
288
+ },
289
+ preview_mint: {
290
+ args: [getSelfArg(), { name: 'shares', type: 'i128' }],
291
+ returns: 'i128',
292
+ code: ['Self::ContractType::preview_mint(e, shares)'],
293
+ },
294
+ mint: {
295
+ args: [
296
+ getSelfArg(),
297
+ { name: 'shares', type: 'i128' },
298
+ { name: 'receiver', type: 'Address' },
299
+ { name: 'from', type: 'Address' },
300
+ { name: 'operator', type: 'Address' },
301
+ ],
302
+ returns: 'i128',
303
+ code: ['Self::ContractType::mint(e, shares, receiver, from, operator)'],
304
+ },
305
+ max_withdraw: {
306
+ args: [getSelfArg(), { name: 'owner', type: 'Address' }],
307
+ returns: 'i128',
308
+ code: ['Self::ContractType::max_withdraw(e, owner)'],
309
+ },
310
+ preview_withdraw: {
311
+ args: [getSelfArg(), { name: 'assets', type: 'i128' }],
312
+ returns: 'i128',
313
+ code: ['Self::ContractType::preview_withdraw(e, assets)'],
314
+ },
315
+ withdraw: {
316
+ args: [
317
+ getSelfArg(),
318
+ { name: 'assets', type: 'i128' },
319
+ { name: 'receiver', type: 'Address' },
320
+ { name: 'owner', type: 'Address' },
321
+ { name: 'operator', type: 'Address' },
322
+ ],
323
+ returns: 'i128',
324
+ code: ['Self::ContractType::withdraw(e, assets, receiver, owner, operator)'],
325
+ },
326
+ max_redeem: {
327
+ args: [getSelfArg(), { name: 'owner', type: 'Address' }],
328
+ returns: 'i128',
329
+ code: ['Self::ContractType::max_redeem(e, owner)'],
330
+ },
331
+ preview_redeem: {
332
+ args: [getSelfArg(), { name: 'shares', type: 'i128' }],
333
+ returns: 'i128',
334
+ code: ['Self::ContractType::preview_redeem(e, shares)'],
335
+ },
336
+ redeem: {
337
+ args: [
338
+ getSelfArg(),
339
+ { name: 'shares', type: 'i128' },
340
+ { name: 'receiver', type: 'Address' },
341
+ { name: 'owner', type: 'Address' },
342
+ { name: 'operator', type: 'Address' },
343
+ ],
344
+ returns: 'i128',
345
+ code: ['Self::ContractType::redeem(e, shares, receiver, owner, operator)'],
346
+ },
347
+ });
348
+
349
+ const fungibleTokenTraitFunctions = pickKeys(functions, [
350
+ 'total_supply',
351
+ 'balance',
352
+ 'allowance',
353
+ 'transfer',
354
+ 'transfer_from',
355
+ 'approve',
356
+ 'decimals',
357
+ 'name',
358
+ 'symbol',
359
+ ]);
360
+
361
+ const fungibleVaultTraitFunctions = pickKeys(functions, [
362
+ 'query_asset',
363
+ 'total_assets',
364
+ 'convert_to_shares',
365
+ 'convert_to_assets',
366
+ 'max_deposit',
367
+ 'preview_deposit',
368
+ 'deposit',
369
+ 'max_mint',
370
+ 'preview_mint',
371
+ 'mint',
372
+ 'max_withdraw',
373
+ 'preview_withdraw',
374
+ 'withdraw',
375
+ 'max_redeem',
376
+ 'preview_redeem',
377
+ 'redeem',
378
+ ]);
package/src/zip-rust.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import JSZip from 'jszip';
2
+ import { defaults as accountDefaults } from './account';
2
3
  import type { GenericOptions } from './build-generic';
3
4
  import type { Contract } from './contract';
4
5
  import { printContract, removeCreateLevelAttributes } from './print';
@@ -6,8 +7,10 @@ import {
6
7
  contractOptionsToContractName,
7
8
  createRustLibFile,
8
9
  getRequiredLibDependencies,
10
+ printAccountRustTest,
9
11
  printContractCargo,
10
12
  printRustNameTest,
13
+ printVaultRustTest,
11
14
  workspaceCargo,
12
15
  } from './zip-shared';
13
16
 
@@ -30,6 +33,19 @@ const gitIgnore = `\
30
33
  target/
31
34
  `;
32
35
 
36
+ // Most contracts get the generic name assertion; kinds whose constructor takes
37
+ // more than plain addresses need a template that can satisfy it.
38
+ const printTest = (c: Contract, opts: GenericOptions) => {
39
+ switch (opts?.kind) {
40
+ case 'Account':
41
+ return printAccountRustTest(c, opts.policy ?? accountDefaults.policy);
42
+ case 'Vault':
43
+ return printVaultRustTest(c);
44
+ default:
45
+ return printRustNameTest(c);
46
+ }
47
+ };
48
+
33
49
  export const createRustZipEnvironment = (c: Contract, opts: GenericOptions) => {
34
50
  const zip = new JSZip();
35
51
 
@@ -37,7 +53,7 @@ export const createRustZipEnvironment = (c: Contract, opts: GenericOptions) => {
37
53
  const requiredLibDeps = getRequiredLibDependencies(c);
38
54
 
39
55
  zip.file(`contracts/${contractName}/src/contract.rs`, removeCreateLevelAttributes(printContract(c)));
40
- zip.file(`contracts/${contractName}/src/test.rs`, printRustNameTest(c));
56
+ zip.file(`contracts/${contractName}/src/test.rs`, printTest(c, opts));
41
57
  zip.file(`contracts/${contractName}/src/lib.rs`, createRustLibFile);
42
58
  zip.file(`contracts/${contractName}/Cargo.toml`, printContractCargo(contractName, requiredLibDeps));
43
59
  zip.file('Cargo.toml', workspaceCargo(requiredLibDeps));