@openzeppelin/wizard-stellar 0.6.2 → 0.6.3
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.
- package/dist/api.d.ts +3 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +7 -1
- package/dist/api.js.map +1 -1
- package/dist/build-generic.d.ts +4 -0
- package/dist/build-generic.d.ts.map +1 -1
- package/dist/build-generic.js +3 -0
- package/dist/build-generic.js.map +1 -1
- package/dist/generate/sources.d.ts.map +1 -1
- package/dist/generate/sources.js +6 -0
- package/dist/generate/sources.js.map +1 -1
- package/dist/generate/vault.d.ts +3 -0
- package/dist/generate/vault.d.ts.map +1 -0
- package/dist/generate/vault.js +21 -0
- package/dist/generate/vault.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/kind.js +1 -0
- package/dist/kind.js.map +1 -1
- package/dist/utils/compile-test.d.ts.map +1 -1
- package/dist/utils/compile-test.js +4 -1
- package/dist/utils/compile-test.js.map +1 -1
- package/dist/vault.d.ts +16 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +334 -0
- package/dist/vault.js.map +1 -0
- package/dist/zip-rust.d.ts.map +1 -1
- package/dist/zip-rust.js +1 -1
- package/dist/zip-rust.js.map +1 -1
- package/dist/zip-shared.d.ts +1 -0
- package/dist/zip-shared.d.ts.map +1 -1
- package/dist/zip-shared.js +60 -1
- package/dist/zip-shared.js.map +1 -1
- package/package.json +1 -1
- package/src/api.ts +13 -0
- package/src/build-generic.ts +6 -0
- package/src/generate/sources.ts +7 -0
- package/src/generate/vault.ts +21 -0
- package/src/index.ts +2 -0
- package/src/kind.ts +1 -0
- package/src/utils/compile-test.ts +5 -1
- package/src/vault.ts +378 -0
- package/src/zip-rust.ts +5 -1
- package/src/zip-shared.ts +62 -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
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getRequiredLibDependencies,
|
|
9
9
|
printContractCargo,
|
|
10
10
|
printRustNameTest,
|
|
11
|
+
printVaultRustTest,
|
|
11
12
|
workspaceCargo,
|
|
12
13
|
} from './zip-shared';
|
|
13
14
|
|
|
@@ -37,7 +38,10 @@ export const createRustZipEnvironment = (c: Contract, opts: GenericOptions) => {
|
|
|
37
38
|
const requiredLibDeps = getRequiredLibDependencies(c);
|
|
38
39
|
|
|
39
40
|
zip.file(`contracts/${contractName}/src/contract.rs`, removeCreateLevelAttributes(printContract(c)));
|
|
40
|
-
zip.file(
|
|
41
|
+
zip.file(
|
|
42
|
+
`contracts/${contractName}/src/test.rs`,
|
|
43
|
+
opts?.kind === 'Vault' ? printVaultRustTest(c) : printRustNameTest(c),
|
|
44
|
+
);
|
|
41
45
|
zip.file(`contracts/${contractName}/src/lib.rs`, createRustLibFile);
|
|
42
46
|
zip.file(`contracts/${contractName}/Cargo.toml`, printContractCargo(contractName, requiredLibDeps));
|
|
43
47
|
zip.file('Cargo.toml', workspaceCargo(requiredLibDeps));
|
package/src/zip-shared.ts
CHANGED
|
@@ -39,6 +39,68 @@ fn initial_state() {
|
|
|
39
39
|
// Add more tests bellow
|
|
40
40
|
`;
|
|
41
41
|
|
|
42
|
+
// The vault's constructor takes an underlying asset address and derives its own
|
|
43
|
+
// decimals by calling into that asset contract. A generated address would have
|
|
44
|
+
// no `decimals()` to call, so the test deploys a minimal mock fungible asset
|
|
45
|
+
// first and registers the vault against it. A single-element tuple needs a
|
|
46
|
+
// trailing comma in Rust.
|
|
47
|
+
export const printVaultRustTest = (c: Pick<Contract, 'constructorArgs' | 'name'>) => {
|
|
48
|
+
const registerArgs = (c.constructorArgs || []).map(arg =>
|
|
49
|
+
arg.name === 'asset' ? 'asset_address.clone()' : 'Address::generate(&env)',
|
|
50
|
+
);
|
|
51
|
+
const registerTuple = `(${registerArgs.join(', ')}${registerArgs.length === 1 ? ',' : ''})`;
|
|
52
|
+
|
|
53
|
+
return `#![cfg(test)]
|
|
54
|
+
|
|
55
|
+
extern crate std;
|
|
56
|
+
|
|
57
|
+
use soroban_sdk::{
|
|
58
|
+
contract, contractimpl, testutils::Address as _, Address, Env, MuxedAddress, String,
|
|
59
|
+
};
|
|
60
|
+
use stellar_tokens::fungible::{Base, FungibleToken};
|
|
61
|
+
|
|
62
|
+
use crate::contract::{ ${c.name}, ${c.name}Client };
|
|
63
|
+
|
|
64
|
+
// Mock asset contract: a minimal fungible token used as the vault's underlying asset.
|
|
65
|
+
#[contract]
|
|
66
|
+
pub struct MockAssetContract;
|
|
67
|
+
|
|
68
|
+
#[contractimpl]
|
|
69
|
+
impl MockAssetContract {
|
|
70
|
+
pub fn __constructor(e: &Env, initial_supply: i128, admin: Address) {
|
|
71
|
+
Base::set_metadata(
|
|
72
|
+
e,
|
|
73
|
+
18,
|
|
74
|
+
String::from_str(e, "Mock Asset Token"),
|
|
75
|
+
String::from_str(e, "MAT"),
|
|
76
|
+
);
|
|
77
|
+
Base::mint(e, &admin, initial_supply);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#[contractimpl(contracttrait)]
|
|
82
|
+
impl FungibleToken for MockAssetContract {
|
|
83
|
+
type ContractType = Base;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[test]
|
|
87
|
+
fn initial_state() {
|
|
88
|
+
let env = Env::default();
|
|
89
|
+
|
|
90
|
+
let admin = Address::generate(&env);
|
|
91
|
+
let asset_address = env.register(MockAssetContract, (1_000_000_000i128, admin));
|
|
92
|
+
|
|
93
|
+
let contract_addr = env.register(${c.name}, ${registerTuple});
|
|
94
|
+
let client = ${c.name}Client::new(&env, &contract_addr);
|
|
95
|
+
|
|
96
|
+
assert_eq!(client.query_asset(), asset_address);
|
|
97
|
+
assert_eq!(client.name(), String::from_str(&env, "${c.name}"));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Add more tests bellow
|
|
101
|
+
`;
|
|
102
|
+
};
|
|
103
|
+
|
|
42
104
|
const libDependencies = [
|
|
43
105
|
'stellar-tokens',
|
|
44
106
|
'stellar-access',
|