@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/zip-shared.ts CHANGED
@@ -39,9 +39,302 @@ 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
+
104
+ // A smart account contacts its policy and verifier contracts while it is being
105
+ // constructed: `add_context_rule` installs every policy and canonicalizes every
106
+ // external signer key through its verifier. A test that registers the account
107
+ // therefore needs real contracts at those addresses, so the mocks below wrap the
108
+ // library's own logic, the same way the reference examples do.
109
+ const accountPolicies = {
110
+ 'simple-threshold': {
111
+ struct: 'MockSimpleThresholdPolicy',
112
+ module: 'simple_threshold',
113
+ params: 'SimpleThresholdAccountParams',
114
+ },
115
+ 'weighted-threshold': {
116
+ struct: 'MockWeightedThresholdPolicy',
117
+ module: 'weighted_threshold',
118
+ params: 'WeightedThresholdAccountParams',
119
+ },
120
+ } as const;
121
+
122
+ export type AccountPolicy = keyof typeof accountPolicies | false;
123
+
124
+ // How many of each signer type the generated test deploys with. `weights` must
125
+ // have one entry per assembled signer, so the counts drive that too.
126
+ const testSignerCounts = {
127
+ delegated_signers: 2,
128
+ ed25519_keys: 1,
129
+ webauthn_keys: 1,
130
+ } as const;
131
+
132
+ const testConstructorArgValues: Record<string, (signerCount: number) => string> = {
133
+ delegated_signers: () => 'Vec::from_array(&env, [Address::generate(&env), Address::generate(&env)])',
134
+ ed25519_verifier: () => 'ed25519_verifier',
135
+ ed25519_keys: () => 'Vec::from_array(&env, [Bytes::from_slice(&env, &[1u8; 32])])',
136
+ webauthn_verifier: () => 'webauthn_verifier',
137
+ // A WebAuthn key is the 65-byte secp256r1 public key followed by an optional
138
+ // credential id; canonicalization reads the first 65 bytes.
139
+ webauthn_keys: () => 'Vec::from_array(&env, [Bytes::from_slice(&env, &[2u8; 65])])',
140
+ weights: signerCount => `Vec::from_array(&env, [${Array<string>(signerCount).fill('1u32').join(', ')}])`,
141
+ threshold: () => '1u32',
142
+ threshold_policy: () => 'policy',
143
+ };
144
+
145
+ const MOCK_ED25519_VERIFIER = `
146
+ // Minimal Ed25519 verifier contract, for testing purposes only: a real
147
+ // deployment passes the address of a verifier contract it trusts.
148
+ #[contract]
149
+ pub struct MockEd25519Verifier;
150
+
151
+ #[contractimpl]
152
+ impl Verifier for MockEd25519Verifier {
153
+ type KeyData = BytesN<32>;
154
+ type SigData = BytesN<64>;
155
+
156
+ fn verify(
157
+ e: &Env,
158
+ signature_payload: Bytes,
159
+ key_data: BytesN<32>,
160
+ sig_data: BytesN<64>,
161
+ ) -> bool {
162
+ ed25519::verify(e, &signature_payload, &key_data, &sig_data)
163
+ }
164
+
165
+ fn canonicalize_key(e: &Env, key_data: BytesN<32>) -> Bytes {
166
+ ed25519::canonicalize_key(e, &key_data)
167
+ }
168
+
169
+ fn batch_canonicalize_key(e: &Env, keys_data: Vec<BytesN<32>>) -> Vec<Bytes> {
170
+ ed25519::batch_canonicalize_key(e, &keys_data)
171
+ }
172
+ }
173
+ `;
174
+
175
+ const MOCK_WEBAUTHN_VERIFIER = `
176
+ // Minimal WebAuthn (passkey) verifier contract, for testing purposes only: a
177
+ // real deployment passes the address of a verifier contract it trusts.
178
+ #[contract]
179
+ pub struct MockWebauthnVerifier;
180
+
181
+ #[contractimpl]
182
+ impl Verifier for MockWebauthnVerifier {
183
+ type KeyData = Bytes;
184
+ type SigData = Bytes;
185
+
186
+ fn verify(e: &Env, signature_payload: Bytes, key_data: Bytes, sig_data: Bytes) -> bool {
187
+ let sig_struct =
188
+ WebAuthnSigData::from_xdr(e, &sig_data).expect("WebAuthnSigData with correct format");
189
+ let pub_key: BytesN<65> =
190
+ extract_from_bytes(e, &key_data, 0..65).expect("65-byte public key to be extracted");
191
+
192
+ webauthn::verify(e, &signature_payload, &pub_key, &sig_struct)
193
+ }
194
+
195
+ fn canonicalize_key(e: &Env, key_data: Bytes) -> Bytes {
196
+ webauthn::canonicalize_key(e, &key_data)
197
+ }
198
+
199
+ fn batch_canonicalize_key(e: &Env, keys_data: Vec<Bytes>) -> Vec<Bytes> {
200
+ webauthn::batch_canonicalize_key(e, &keys_data)
201
+ }
202
+ }
203
+ `;
204
+
205
+ const printMockPolicyContract = (policy: keyof typeof accountPolicies) => {
206
+ const { struct, module, params } = accountPolicies[policy];
207
+
208
+ return `
209
+ // Minimal policy contract, for testing purposes only: the account installs its
210
+ // policies during construction, so the test needs one deployed at that address.
211
+ // A real deployment passes the address of a policy contract it trusts.
212
+ #[contract]
213
+ pub struct ${struct};
214
+
215
+ #[contractimpl]
216
+ impl Policy for ${struct} {
217
+ type AccountParams = ${module}::${params};
218
+
219
+ fn enforce(
220
+ e: &Env,
221
+ context: Context,
222
+ authenticated_signers: Vec<Signer>,
223
+ context_rule: ContextRule,
224
+ smart_account: Address,
225
+ ) {
226
+ ${module}::enforce(e, &context, &authenticated_signers, &context_rule, &smart_account)
227
+ }
228
+
229
+ fn install(
230
+ e: &Env,
231
+ install_params: Self::AccountParams,
232
+ context_rule: ContextRule,
233
+ smart_account: Address,
234
+ ) {
235
+ ${module}::install(e, &install_params, &context_rule, &smart_account)
236
+ }
237
+
238
+ fn uninstall(e: &Env, context_rule: ContextRule, smart_account: Address) {
239
+ ${module}::uninstall(e, &context_rule, &smart_account)
240
+ }
241
+ }
242
+ `;
243
+ };
244
+
245
+ export const printAccountRustTest = (c: Pick<Contract, 'constructorArgs' | 'name'>, policy: AccountPolicy) => {
246
+ const argNames = (c.constructorArgs || []).map(arg => arg.name);
247
+ const signerCount = Object.entries(testSignerCounts).reduce(
248
+ (total, [argName, count]) => (argNames.includes(argName) ? total + count : total),
249
+ 0,
250
+ );
251
+
252
+ const hasEd25519 = argNames.includes('ed25519_keys');
253
+ const hasWebauthn = argNames.includes('webauthn_keys');
254
+ const hasPolicy = policy !== false;
255
+
256
+ // Every argument is spelled on its own line: the assembled tuple is otherwise
257
+ // far too long to read.
258
+ const registerArgs = argNames
259
+ .map(argName => ` ${testConstructorArgValues[argName]!(signerCount)},`)
260
+ .join('\n');
261
+
262
+ const hasMocks = hasEd25519 || hasWebauthn || hasPolicy;
263
+
264
+ const sorobanImports = [
265
+ ...(hasPolicy ? ['auth::Context'] : []),
266
+ ...(hasMocks ? ['contract', 'contractimpl'] : []),
267
+ 'testutils::Address as _',
268
+ ...(hasWebauthn ? ['xdr::FromXdr'] : []),
269
+ 'Address',
270
+ ...(hasEd25519 || hasWebauthn ? ['Bytes', 'BytesN'] : []),
271
+ 'Env',
272
+ 'Vec',
273
+ ];
274
+
275
+ // Kept on separate lines so that enabling both verifiers does not import
276
+ // `Verifier` twice.
277
+ const libImports = [
278
+ ...(hasPolicy
279
+ ? [
280
+ `use stellar_accounts::policies::{ ${accountPolicies[policy].module}, Policy };`,
281
+ 'use stellar_accounts::smart_account::{ ContextRule, Signer };',
282
+ ]
283
+ : []),
284
+ ...(hasEd25519 || hasWebauthn ? ['use stellar_accounts::verifiers::Verifier;'] : []),
285
+ ...(hasEd25519 ? ['use stellar_accounts::verifiers::ed25519;'] : []),
286
+ ...(hasWebauthn
287
+ ? [
288
+ 'use stellar_accounts::verifiers::utils::extract_from_bytes;',
289
+ 'use stellar_accounts::verifiers::webauthn::{ self, WebAuthnSigData };',
290
+ ]
291
+ : []),
292
+ ];
293
+
294
+ const mockContracts = [
295
+ ...(hasEd25519 ? [MOCK_ED25519_VERIFIER] : []),
296
+ ...(hasWebauthn ? [MOCK_WEBAUTHN_VERIFIER] : []),
297
+ ...(hasPolicy ? [printMockPolicyContract(policy)] : []),
298
+ ];
299
+
300
+ const registrations = [
301
+ ...(hasEd25519 ? [' let ed25519_verifier = env.register(MockEd25519Verifier, ());'] : []),
302
+ ...(hasWebauthn ? [' let webauthn_verifier = env.register(MockWebauthnVerifier, ());'] : []),
303
+ ...(hasPolicy ? [` let policy = env.register(${accountPolicies[policy].struct}, ());`] : []),
304
+ ];
305
+
306
+ return `#![cfg(test)]
307
+
308
+ extern crate std;
309
+
310
+ use soroban_sdk::{ ${sorobanImports.join(', ')} };
311
+ ${libImports.map(libImport => `${libImport}\n`).join('')}
312
+ use crate::contract::{ ${c.name}, ${c.name}Client };
313
+ ${mockContracts.join('')}
314
+ #[test]
315
+ fn initial_state() {
316
+ let env = Env::default();
317
+ ${hasPolicy ? ' env.mock_all_auths();\n' : ''}
318
+ ${registrations.map(registration => `${registration}\n`).join('')}
319
+ let contract_addr = env.register(
320
+ ${c.name},
321
+ (
322
+ ${registerArgs}
323
+ ),
324
+ );
325
+ let client = ${c.name}Client::new(&env, &contract_addr);
326
+
327
+ assert_eq!(client.get_context_rules_count(), 1);
328
+ }
329
+
330
+ // Add more tests below
331
+ `;
332
+ };
333
+
42
334
  const libDependencies = [
43
335
  'stellar-tokens',
44
336
  'stellar-access',
337
+ 'stellar-accounts',
45
338
  'stellar-contract-utils',
46
339
  'stellar-governance',
47
340
  'stellar-macros',