@openzeppelin/wizard-stellar 0.6.3 → 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 (60) 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 +7 -0
  11. package/dist/api.d.ts.map +1 -1
  12. package/dist/api.js +6 -1
  13. package/dist/api.js.map +1 -1
  14. package/dist/build-generic.d.ts +4 -0
  15. package/dist/build-generic.d.ts.map +1 -1
  16. package/dist/build-generic.js +3 -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 +6 -0
  28. package/dist/generate/sources.js.map +1 -1
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +8 -6
  32. package/dist/index.js.map +1 -1
  33. package/dist/kind.js +1 -0
  34. package/dist/kind.js.map +1 -1
  35. package/dist/print.js +39 -9
  36. package/dist/print.js.map +1 -1
  37. package/dist/utils/compile-test.d.ts.map +1 -1
  38. package/dist/utils/compile-test.js +1 -0
  39. package/dist/utils/compile-test.js.map +1 -1
  40. package/dist/zip-rust.d.ts.map +1 -1
  41. package/dist/zip-rust.js +14 -1
  42. package/dist/zip-rust.js.map +1 -1
  43. package/dist/zip-shared.d.ts +14 -0
  44. package/dist/zip-shared.d.ts.map +1 -1
  45. package/dist/zip-shared.js +212 -1
  46. package/dist/zip-shared.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/account.ts +319 -0
  49. package/src/add-upgradeable.ts +29 -0
  50. package/src/api.ts +12 -0
  51. package/src/build-generic.ts +6 -0
  52. package/src/contract.ts +24 -2
  53. package/src/generate/account.ts +21 -0
  54. package/src/generate/sources.ts +7 -0
  55. package/src/index.ts +2 -0
  56. package/src/kind.ts +1 -0
  57. package/src/print.ts +47 -18
  58. package/src/utils/compile-test.ts +1 -0
  59. package/src/zip-rust.ts +16 -4
  60. package/src/zip-shared.ts +231 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.workspaceCargo = exports.createRustLibFile = exports.printContractCargo = exports.addDependenciesWith = exports.printVaultRustTest = exports.printRustNameTest = exports.contractOptionsToContractName = void 0;
3
+ exports.workspaceCargo = exports.createRustLibFile = exports.printContractCargo = exports.addDependenciesWith = exports.printAccountRustTest = exports.printVaultRustTest = exports.printRustNameTest = exports.contractOptionsToContractName = void 0;
4
4
  exports.getAddressArgs = getAddressArgs;
5
5
  exports.getRequiredLibDependencies = getRequiredLibDependencies;
6
6
  const version_1 = require("./utils/version");
@@ -98,9 +98,220 @@ fn initial_state() {
98
98
  `;
99
99
  };
100
100
  exports.printVaultRustTest = printVaultRustTest;
101
+ // A smart account contacts its policy and verifier contracts while it is being
102
+ // constructed: `add_context_rule` installs every policy and canonicalizes every
103
+ // external signer key through its verifier. A test that registers the account
104
+ // therefore needs real contracts at those addresses, so the mocks below wrap the
105
+ // library's own logic, the same way the reference examples do.
106
+ const accountPolicies = {
107
+ 'simple-threshold': {
108
+ struct: 'MockSimpleThresholdPolicy',
109
+ module: 'simple_threshold',
110
+ params: 'SimpleThresholdAccountParams',
111
+ },
112
+ 'weighted-threshold': {
113
+ struct: 'MockWeightedThresholdPolicy',
114
+ module: 'weighted_threshold',
115
+ params: 'WeightedThresholdAccountParams',
116
+ },
117
+ };
118
+ // How many of each signer type the generated test deploys with. `weights` must
119
+ // have one entry per assembled signer, so the counts drive that too.
120
+ const testSignerCounts = {
121
+ delegated_signers: 2,
122
+ ed25519_keys: 1,
123
+ webauthn_keys: 1,
124
+ };
125
+ const testConstructorArgValues = {
126
+ delegated_signers: () => 'Vec::from_array(&env, [Address::generate(&env), Address::generate(&env)])',
127
+ ed25519_verifier: () => 'ed25519_verifier',
128
+ ed25519_keys: () => 'Vec::from_array(&env, [Bytes::from_slice(&env, &[1u8; 32])])',
129
+ webauthn_verifier: () => 'webauthn_verifier',
130
+ // A WebAuthn key is the 65-byte secp256r1 public key followed by an optional
131
+ // credential id; canonicalization reads the first 65 bytes.
132
+ webauthn_keys: () => 'Vec::from_array(&env, [Bytes::from_slice(&env, &[2u8; 65])])',
133
+ weights: signerCount => `Vec::from_array(&env, [${Array(signerCount).fill('1u32').join(', ')}])`,
134
+ threshold: () => '1u32',
135
+ threshold_policy: () => 'policy',
136
+ };
137
+ const MOCK_ED25519_VERIFIER = `
138
+ // Minimal Ed25519 verifier contract, for testing purposes only: a real
139
+ // deployment passes the address of a verifier contract it trusts.
140
+ #[contract]
141
+ pub struct MockEd25519Verifier;
142
+
143
+ #[contractimpl]
144
+ impl Verifier for MockEd25519Verifier {
145
+ type KeyData = BytesN<32>;
146
+ type SigData = BytesN<64>;
147
+
148
+ fn verify(
149
+ e: &Env,
150
+ signature_payload: Bytes,
151
+ key_data: BytesN<32>,
152
+ sig_data: BytesN<64>,
153
+ ) -> bool {
154
+ ed25519::verify(e, &signature_payload, &key_data, &sig_data)
155
+ }
156
+
157
+ fn canonicalize_key(e: &Env, key_data: BytesN<32>) -> Bytes {
158
+ ed25519::canonicalize_key(e, &key_data)
159
+ }
160
+
161
+ fn batch_canonicalize_key(e: &Env, keys_data: Vec<BytesN<32>>) -> Vec<Bytes> {
162
+ ed25519::batch_canonicalize_key(e, &keys_data)
163
+ }
164
+ }
165
+ `;
166
+ const MOCK_WEBAUTHN_VERIFIER = `
167
+ // Minimal WebAuthn (passkey) verifier contract, for testing purposes only: a
168
+ // real deployment passes the address of a verifier contract it trusts.
169
+ #[contract]
170
+ pub struct MockWebauthnVerifier;
171
+
172
+ #[contractimpl]
173
+ impl Verifier for MockWebauthnVerifier {
174
+ type KeyData = Bytes;
175
+ type SigData = Bytes;
176
+
177
+ fn verify(e: &Env, signature_payload: Bytes, key_data: Bytes, sig_data: Bytes) -> bool {
178
+ let sig_struct =
179
+ WebAuthnSigData::from_xdr(e, &sig_data).expect("WebAuthnSigData with correct format");
180
+ let pub_key: BytesN<65> =
181
+ extract_from_bytes(e, &key_data, 0..65).expect("65-byte public key to be extracted");
182
+
183
+ webauthn::verify(e, &signature_payload, &pub_key, &sig_struct)
184
+ }
185
+
186
+ fn canonicalize_key(e: &Env, key_data: Bytes) -> Bytes {
187
+ webauthn::canonicalize_key(e, &key_data)
188
+ }
189
+
190
+ fn batch_canonicalize_key(e: &Env, keys_data: Vec<Bytes>) -> Vec<Bytes> {
191
+ webauthn::batch_canonicalize_key(e, &keys_data)
192
+ }
193
+ }
194
+ `;
195
+ const printMockPolicyContract = (policy) => {
196
+ const { struct, module, params } = accountPolicies[policy];
197
+ return `
198
+ // Minimal policy contract, for testing purposes only: the account installs its
199
+ // policies during construction, so the test needs one deployed at that address.
200
+ // A real deployment passes the address of a policy contract it trusts.
201
+ #[contract]
202
+ pub struct ${struct};
203
+
204
+ #[contractimpl]
205
+ impl Policy for ${struct} {
206
+ type AccountParams = ${module}::${params};
207
+
208
+ fn enforce(
209
+ e: &Env,
210
+ context: Context,
211
+ authenticated_signers: Vec<Signer>,
212
+ context_rule: ContextRule,
213
+ smart_account: Address,
214
+ ) {
215
+ ${module}::enforce(e, &context, &authenticated_signers, &context_rule, &smart_account)
216
+ }
217
+
218
+ fn install(
219
+ e: &Env,
220
+ install_params: Self::AccountParams,
221
+ context_rule: ContextRule,
222
+ smart_account: Address,
223
+ ) {
224
+ ${module}::install(e, &install_params, &context_rule, &smart_account)
225
+ }
226
+
227
+ fn uninstall(e: &Env, context_rule: ContextRule, smart_account: Address) {
228
+ ${module}::uninstall(e, &context_rule, &smart_account)
229
+ }
230
+ }
231
+ `;
232
+ };
233
+ const printAccountRustTest = (c, policy) => {
234
+ const argNames = (c.constructorArgs || []).map(arg => arg.name);
235
+ const signerCount = Object.entries(testSignerCounts).reduce((total, [argName, count]) => (argNames.includes(argName) ? total + count : total), 0);
236
+ const hasEd25519 = argNames.includes('ed25519_keys');
237
+ const hasWebauthn = argNames.includes('webauthn_keys');
238
+ const hasPolicy = policy !== false;
239
+ // Every argument is spelled on its own line: the assembled tuple is otherwise
240
+ // far too long to read.
241
+ const registerArgs = argNames
242
+ .map(argName => ` ${testConstructorArgValues[argName](signerCount)},`)
243
+ .join('\n');
244
+ const hasMocks = hasEd25519 || hasWebauthn || hasPolicy;
245
+ const sorobanImports = [
246
+ ...(hasPolicy ? ['auth::Context'] : []),
247
+ ...(hasMocks ? ['contract', 'contractimpl'] : []),
248
+ 'testutils::Address as _',
249
+ ...(hasWebauthn ? ['xdr::FromXdr'] : []),
250
+ 'Address',
251
+ ...(hasEd25519 || hasWebauthn ? ['Bytes', 'BytesN'] : []),
252
+ 'Env',
253
+ 'Vec',
254
+ ];
255
+ // Kept on separate lines so that enabling both verifiers does not import
256
+ // `Verifier` twice.
257
+ const libImports = [
258
+ ...(hasPolicy
259
+ ? [
260
+ `use stellar_accounts::policies::{ ${accountPolicies[policy].module}, Policy };`,
261
+ 'use stellar_accounts::smart_account::{ ContextRule, Signer };',
262
+ ]
263
+ : []),
264
+ ...(hasEd25519 || hasWebauthn ? ['use stellar_accounts::verifiers::Verifier;'] : []),
265
+ ...(hasEd25519 ? ['use stellar_accounts::verifiers::ed25519;'] : []),
266
+ ...(hasWebauthn
267
+ ? [
268
+ 'use stellar_accounts::verifiers::utils::extract_from_bytes;',
269
+ 'use stellar_accounts::verifiers::webauthn::{ self, WebAuthnSigData };',
270
+ ]
271
+ : []),
272
+ ];
273
+ const mockContracts = [
274
+ ...(hasEd25519 ? [MOCK_ED25519_VERIFIER] : []),
275
+ ...(hasWebauthn ? [MOCK_WEBAUTHN_VERIFIER] : []),
276
+ ...(hasPolicy ? [printMockPolicyContract(policy)] : []),
277
+ ];
278
+ const registrations = [
279
+ ...(hasEd25519 ? [' let ed25519_verifier = env.register(MockEd25519Verifier, ());'] : []),
280
+ ...(hasWebauthn ? [' let webauthn_verifier = env.register(MockWebauthnVerifier, ());'] : []),
281
+ ...(hasPolicy ? [` let policy = env.register(${accountPolicies[policy].struct}, ());`] : []),
282
+ ];
283
+ return `#![cfg(test)]
284
+
285
+ extern crate std;
286
+
287
+ use soroban_sdk::{ ${sorobanImports.join(', ')} };
288
+ ${libImports.map(libImport => `${libImport}\n`).join('')}
289
+ use crate::contract::{ ${c.name}, ${c.name}Client };
290
+ ${mockContracts.join('')}
291
+ #[test]
292
+ fn initial_state() {
293
+ let env = Env::default();
294
+ ${hasPolicy ? ' env.mock_all_auths();\n' : ''}
295
+ ${registrations.map(registration => `${registration}\n`).join('')}
296
+ let contract_addr = env.register(
297
+ ${c.name},
298
+ (
299
+ ${registerArgs}
300
+ ),
301
+ );
302
+ let client = ${c.name}Client::new(&env, &contract_addr);
303
+
304
+ assert_eq!(client.get_context_rules_count(), 1);
305
+ }
306
+
307
+ // Add more tests below
308
+ `;
309
+ };
310
+ exports.printAccountRustTest = printAccountRustTest;
101
311
  const libDependencies = [
102
312
  'stellar-tokens',
103
313
  'stellar-access',
314
+ 'stellar-accounts',
104
315
  'stellar-contract-utils',
105
316
  'stellar-governance',
106
317
  'stellar-macros',
@@ -1 +1 @@
1
- {"version":3,"file":"zip-shared.js","sourceRoot":"","sources":["../src/zip-shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"zip-shared.js","sourceRoot":"","sources":["../src/zip-shared.ts"],"names":[],"mappings":";;;AAYA,wCAIC;AA6UD,gEAGC;AA/VD,6CAA6E;AAE7E,SAAS,iBAAiB,CAAC,MAAc;IACvC,OAAO,MAAM;SACV,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;SAC1B,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;SACjB,WAAW,EAAE,CAAC;AACnB,CAAC;AAEY,QAAA,6BAA6B,GAAG,iBAAiB,CAAC;AAE/D,SAAgB,cAAc,CAAC,CAAoC;IACjE,OAAO,CAAC,CAAC,CAAC,eAAe,IAAI,EAAE,CAAC;SAC7B,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,SAAS,CAAC;SACnE,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAEM,MAAM,iBAAiB,GAAG,CAAC,CAA6C,EAAE,EAAE,CAAC;;;;qBAI/D,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,EAAE;;yBAEhE,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;;;;;;uCAMH,CAAC,CAAC,IAAI,MAAM,cAAc,CAAC,CAAC,CAAC;KAC7D,GAAG,CAAC,GAAG,EAAE,CAAC,yBAAyB,CAAC;KACpC,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;mBACzC,CAAC,CAAC,IAAI;;wDAE+B,CAAC,CAAC,IAAI;;;;CAI7D,CAAC;AArBW,QAAA,iBAAiB,qBAqB5B;AAEF,gFAAgF;AAChF,+EAA+E;AAC/E,6EAA6E;AAC7E,2EAA2E;AAC3E,0BAA0B;AACnB,MAAM,kBAAkB,GAAG,CAAC,CAA6C,EAAE,EAAE;IAClF,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CACvD,GAAG,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,yBAAyB,CAC3E,CAAC;IACF,MAAM,aAAa,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IAE5F,OAAO;;;;;;;;;yBASgB,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uCA+BH,CAAC,CAAC,IAAI,KAAK,aAAa;mBAC5C,CAAC,CAAC,IAAI;;;wDAG+B,CAAC,CAAC,IAAI;;;;CAI7D,CAAC;AACF,CAAC,CAAC;AAvDW,QAAA,kBAAkB,sBAuD7B;AAEF,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,iFAAiF;AACjF,+DAA+D;AAC/D,MAAM,eAAe,GAAG;IACtB,kBAAkB,EAAE;QAClB,MAAM,EAAE,2BAA2B;QACnC,MAAM,EAAE,kBAAkB;QAC1B,MAAM,EAAE,8BAA8B;KACvC;IACD,oBAAoB,EAAE;QACpB,MAAM,EAAE,6BAA6B;QACrC,MAAM,EAAE,oBAAoB;QAC5B,MAAM,EAAE,gCAAgC;KACzC;CACO,CAAC;AAIX,+EAA+E;AAC/E,qEAAqE;AACrE,MAAM,gBAAgB,GAAG;IACvB,iBAAiB,EAAE,CAAC;IACpB,YAAY,EAAE,CAAC;IACf,aAAa,EAAE,CAAC;CACR,CAAC;AAEX,MAAM,wBAAwB,GAAoD;IAChF,iBAAiB,EAAE,GAAG,EAAE,CAAC,2EAA2E;IACpG,gBAAgB,EAAE,GAAG,EAAE,CAAC,kBAAkB;IAC1C,YAAY,EAAE,GAAG,EAAE,CAAC,8DAA8D;IAClF,iBAAiB,EAAE,GAAG,EAAE,CAAC,mBAAmB;IAC5C,6EAA6E;IAC7E,4DAA4D;IAC5D,aAAa,EAAE,GAAG,EAAE,CAAC,8DAA8D;IACnF,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,0BAA0B,KAAK,CAAS,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;IACxG,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM;IACvB,gBAAgB,EAAE,GAAG,EAAE,CAAC,QAAQ;CACjC,CAAC;AAEF,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B7B,CAAC;AAEF,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B9B,CAAC;AAEF,MAAM,uBAAuB,GAAG,CAAC,MAAoC,EAAE,EAAE;IACvE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAE3D,OAAO;;;;;aAKI,MAAM;;;kBAGD,MAAM;2BACG,MAAM,KAAK,MAAM;;;;;;;;;UASlC,MAAM;;;;;;;;;UASN,MAAM;;;;UAIN,MAAM;;;CAGf,CAAC;AACF,CAAC,CAAC;AAEK,MAAM,oBAAoB,GAAG,CAAC,CAA6C,EAAE,MAAqB,EAAE,EAAE;IAC3G,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,MAAM,CACzD,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EACjF,CAAC,CACF,CAAC;IAEF,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,MAAM,KAAK,KAAK,CAAC;IAEnC,8EAA8E;IAC9E,wBAAwB;IACxB,MAAM,YAAY,GAAG,QAAQ;SAC1B,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,eAAe,wBAAwB,CAAC,OAAO,CAAE,CAAC,WAAW,CAAC,GAAG,CAAC;SACjF,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,QAAQ,GAAG,UAAU,IAAI,WAAW,IAAI,SAAS,CAAC;IAExD,MAAM,cAAc,GAAG;QACrB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,yBAAyB;QACzB,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,SAAS;QACT,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,KAAK;QACL,KAAK;KACN,CAAC;IAEF,yEAAyE;IACzE,oBAAoB;IACpB,MAAM,UAAU,GAAG;QACjB,GAAG,CAAC,SAAS;YACX,CAAC,CAAC;gBACE,qCAAqC,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,aAAa;gBAChF,+DAA+D;aAChE;YACH,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,WAAW;YACb,CAAC,CAAC;gBACE,6DAA6D;gBAC7D,uEAAuE;aACxE;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;IAEF,MAAM,aAAa,GAAG;QACpB,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACxD,CAAC;IAEF,MAAM,aAAa,GAAG;QACpB,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,mEAAmE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,qEAAqE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,iCAAiC,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAChG,CAAC;IAEF,OAAO;;;;qBAIY,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;EAC5C,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;yBAC/B,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;EACxC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;;;;EAItB,SAAS,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE;EAC9C,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,YAAY,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;UAEvD,CAAC,CAAC,IAAI;;EAEd,YAAY;;;mBAGK,CAAC,CAAC,IAAI;;;;;;CAMxB,CAAC;AACF,CAAC,CAAC;AAvFW,QAAA,oBAAoB,wBAuF/B;AAEF,MAAM,eAAe,GAAG;IACtB,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,wBAAwB;IACxB,oBAAoB;IACpB,gBAAgB;CACR,CAAC;AAEX,2EAA2E;AAC3E,6EAA6E;AAC7E,gFAAgF;AAChF,8EAA8E;AAC9E,2EAA2E;AAC3E,yEAAyE;AACzE,sEAAsE;AACtE,SAAgB,0BAA0B,CAAC,CAA+B;IACxE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACxG,OAAO,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5D,CAAC;AAEM,MAAM,mBAAmB,GAAG,CAAC,eAAuB,EAAE,iBAA2B,EAAE,EAAE,CAC1F,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,UAAU,MAAM,eAAe,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;AAD/E,QAAA,mBAAmB,uBAC4D;AAErF,MAAM,kBAAkB,GAAG,CAAC,oBAA4B,EAAE,eAAkC,EAAE,EAAE,CAAC;UAC9F,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;;;;;;;;;;;;;;EAc9C,IAAA,2BAAmB,EAAC,sBAAsB,EAAE,CAAC,GAAG,eAAe,EAAE,aAAa,CAAC,CAAC;;EAEhF,IAAA,2BAAmB,EAAC,gDAAgD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC;AAjB9E,QAAA,kBAAkB,sBAiB4D;AAE9E,QAAA,iBAAiB,GAAG;;;;;CAKhC,CAAC;AAEK,MAAM,cAAc,GAAG,CAAC,eAAkC,EAAE,EAAE,CAAC;;;;;;;;;;;EAWpE,IAAA,2BAAmB,EAAC,IAAI,kCAAwB,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC,GAAG,IAAA,2BAAmB,EAAC,KAAK,0BAAgB,GAAG,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC;;;;;;;;;;;;;;;CAe5I,CAAC;AA1BW,QAAA,cAAc,kBA0BzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/wizard-stellar",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "A boilerplate generator to get started with OpenZeppelin Stellar Soroban Contracts",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": "https://github.com/OpenZeppelin/contracts-wizard",
package/src/account.ts ADDED
@@ -0,0 +1,319 @@
1
+ import { ContractBuilder } from './contract';
2
+ import { addSelfAuthUpgradeable } from './add-upgradeable';
3
+ import { defineFunctions } from './utils/define-functions';
4
+ import type { CommonOptions } from './common-options';
5
+ import { withCommonDefaults, defaults as commonDefaults } from './common-options';
6
+ import { setInfo } from './set-info';
7
+ import { printContract } from './print';
8
+ import { OptionsError } from './error';
9
+
10
+ export const policyOptions = [false, 'simple-threshold', 'weighted-threshold'] as const;
11
+
12
+ export type Policy = (typeof policyOptions)[number];
13
+
14
+ // Verifier and policy contracts are deployment arguments, not wizard inputs: one
15
+ // wasm then works on both networks. The generated code points at the Stellar
16
+ // Registry, a third-party index of deployed contracts, so that whoever deploys
17
+ // has somewhere to start looking. Vetting the contract is up to them.
18
+ const REGISTRY_HINT = [
19
+ 'Take one you trust from the Stellar Registry:',
20
+ 'https://testnet.rgstry.xyz (testnet) or https://stellar.rgstry.xyz (mainnet).',
21
+ ];
22
+
23
+ // Error code for the single runtime check the generated constructor performs.
24
+ // Kept clear of the library's ranges: `stellar_accounts` uses 3000+ for smart
25
+ // account errors and 3200+ for policy errors.
26
+ const WEIGHTS_LENGTH_MISMATCH_ERROR = 1;
27
+
28
+ export const defaults: Required<AccountOptions> = {
29
+ name: 'MyAccount',
30
+ delegatedSigners: true,
31
+ ed25519Signers: false,
32
+ webauthnSigners: false,
33
+ policy: 'simple-threshold',
34
+ executionEntryPoint: true,
35
+ upgradeable: false,
36
+ info: commonDefaults.info,
37
+ } as const;
38
+
39
+ export function printAccount(opts: AccountOptions = defaults): string {
40
+ return printContract(buildAccount(opts));
41
+ }
42
+
43
+ export interface AccountOptions extends CommonOptions {
44
+ name: string;
45
+ delegatedSigners?: boolean;
46
+ ed25519Signers?: boolean;
47
+ webauthnSigners?: boolean;
48
+ policy?: Policy;
49
+ executionEntryPoint?: boolean;
50
+ upgradeable?: boolean;
51
+ }
52
+
53
+ export function withDefaults(opts: AccountOptions): Required<AccountOptions> {
54
+ return {
55
+ ...opts,
56
+ ...withCommonDefaults(opts),
57
+ delegatedSigners: opts.delegatedSigners ?? defaults.delegatedSigners,
58
+ ed25519Signers: opts.ed25519Signers ?? defaults.ed25519Signers,
59
+ webauthnSigners: opts.webauthnSigners ?? defaults.webauthnSigners,
60
+ policy: opts.policy ?? defaults.policy,
61
+ executionEntryPoint: opts.executionEntryPoint ?? defaults.executionEntryPoint,
62
+ upgradeable: opts.upgradeable ?? defaults.upgradeable,
63
+ };
64
+ }
65
+
66
+ export function buildAccount(opts: AccountOptions): ContractBuilder {
67
+ const c = new ContractBuilder(opts.name);
68
+
69
+ const allOpts = withDefaults(opts);
70
+
71
+ // A context rule may legally hold only policies, but such an account could
72
+ // never authenticate anything, so a signer type is required here.
73
+ if (!allOpts.delegatedSigners && !allOpts.ed25519Signers && !allOpts.webauthnSigners) {
74
+ throw new OptionsError({
75
+ delegatedSigners: 'At least one signer type is required',
76
+ });
77
+ }
78
+
79
+ addBase(c, allOpts);
80
+
81
+ if (allOpts.executionEntryPoint) {
82
+ addExecutionEntryPoint(c);
83
+ }
84
+
85
+ if (allOpts.upgradeable) {
86
+ addSelfAuthUpgradeable(c);
87
+ }
88
+
89
+ setInfo(c, allOpts.info);
90
+
91
+ return c;
92
+ }
93
+
94
+ // External signer types differ only in which verifier contract validates their
95
+ // signatures, so they share a constructor argument shape.
96
+ const externalSigners: { option: 'ed25519Signers' | 'webauthnSigners'; prefix: string; label: string }[] = [
97
+ { option: 'ed25519Signers', prefix: 'ed25519', label: 'Ed25519' },
98
+ { option: 'webauthnSigners', prefix: 'webauthn', label: 'WebAuthn' },
99
+ ];
100
+
101
+ function contextRuleName(policy: Policy): string {
102
+ switch (policy) {
103
+ case false:
104
+ return 'default';
105
+ case 'simple-threshold':
106
+ return 'multisig';
107
+ case 'weighted-threshold':
108
+ return 'weighted-multisig';
109
+ default: {
110
+ const _: never = policy;
111
+ throw new Error('Unknown value for `policy`');
112
+ }
113
+ }
114
+ }
115
+
116
+ function addBase(c: ContractBuilder, opts: Required<AccountOptions>) {
117
+ c.addUseClause('soroban_sdk', 'contract');
118
+ c.addUseClause('soroban_sdk', 'contractimpl');
119
+ c.addUseClause('soroban_sdk', 'Address');
120
+ c.addUseClause('soroban_sdk', 'Env');
121
+ c.addUseClause('soroban_sdk', 'Map');
122
+ c.addUseClause('soroban_sdk', 'String');
123
+ c.addUseClause('soroban_sdk', 'Val');
124
+ c.addUseClause('soroban_sdk', 'Vec');
125
+ c.addUseClause('soroban_sdk::auth', 'Context');
126
+ c.addUseClause('soroban_sdk::auth', 'CustomAccountInterface');
127
+ c.addUseClause('soroban_sdk::crypto', 'Hash');
128
+ c.addUseClause('stellar_accounts::smart_account', 'self', { alias: 'smart_account' });
129
+ c.addUseClause('stellar_accounts::smart_account', 'AuthPayload');
130
+ // `ContextRule` is the return type of several `SmartAccount` methods, which the
131
+ // contracttrait macro needs in scope even though the generated code never
132
+ // names it.
133
+ c.addUseClause('stellar_accounts::smart_account', 'ContextRule');
134
+ c.addUseClause('stellar_accounts::smart_account', 'ContextRuleType');
135
+ c.addUseClause('stellar_accounts::smart_account', 'Signer');
136
+ c.addUseClause('stellar_accounts::smart_account', 'SmartAccount');
137
+ c.addUseClause('stellar_accounts::smart_account', 'SmartAccountError');
138
+
139
+ // The arguments the signers are read from, in assembly order: what `weights`
140
+ // is positionally aligned with.
141
+ const signerArgs: string[] = [];
142
+ const signerLines: string[] = ['let mut signers = Vec::new(e);'];
143
+
144
+ if (opts.delegatedSigners) {
145
+ signerArgs.push('delegated_signers');
146
+ c.addConstructorArgument({ name: 'delegated_signers', type: 'Vec<Address>' });
147
+ signerLines.push(
148
+ 'for account in delegated_signers.iter() {',
149
+ ' signers.push_back(Signer::Delegated(account));',
150
+ '}',
151
+ );
152
+ }
153
+
154
+ for (const { option, prefix, label } of externalSigners) {
155
+ if (!opts[option]) continue;
156
+
157
+ signerArgs.push(`${prefix}_keys`);
158
+ c.addUseClause('soroban_sdk', 'Bytes');
159
+ // Verifier contracts are stateless, immutable and shareable, so one address
160
+ // per signature type serves every key of that type.
161
+ c.addConstructorArgument({
162
+ name: `${prefix}_verifier`,
163
+ type: 'Address',
164
+ comment: [`${label} verifier contract, shared by every ${label} key.`, ...REGISTRY_HINT],
165
+ });
166
+ c.addConstructorArgument({ name: `${prefix}_keys`, type: 'Vec<Bytes>' });
167
+ signerLines.push(
168
+ `for key in ${prefix}_keys.iter() {`,
169
+ ` signers.push_back(Signer::External(${prefix}_verifier.clone(), key));`,
170
+ '}',
171
+ );
172
+ }
173
+
174
+ // Adding the policy after the signers keeps the signer arguments first, which
175
+ // is the order `weights` is aligned with.
176
+ const policyCode = addPolicy(c, opts, signerArgs);
177
+
178
+ c.addConstructorCodeBlock([...signerLines, '']);
179
+ c.addConstructorCodeBlock([...policyCode, '']);
180
+ // The trait's `add_context_rule` requires auth from the account itself, which
181
+ // cannot be satisfied before any rule exists, so the constructor goes through
182
+ // the module-level function instead.
183
+ c.addConstructorCodeBlock([
184
+ 'smart_account::add_context_rule(',
185
+ ' e,',
186
+ ' &ContextRuleType::Default,',
187
+ ` &String::from_str(e, "${contextRuleName(opts.policy)}"),`,
188
+ ' None,',
189
+ ' &signers,',
190
+ ' &policies,',
191
+ ');',
192
+ ]);
193
+
194
+ c.addTraitFunction(
195
+ {
196
+ traitName: 'CustomAccountInterface',
197
+ structName: c.name,
198
+ tags: ['contractimpl'],
199
+ assocType: ['type Error = SmartAccountError;', 'type Signature = AuthPayload;'],
200
+ priority: 0,
201
+ },
202
+ functions.__check_auth,
203
+ );
204
+
205
+ // Every `SmartAccount` method authorizes with
206
+ // `e.current_contract_address().require_auth()`, which routes back into
207
+ // `__check_auth` and the context rules, so the library defaults are kept as is.
208
+ c.addTraitImplBlock({
209
+ traitName: 'SmartAccount',
210
+ structName: c.name,
211
+ tags: ['contractimpl(contracttrait)'],
212
+ priority: 1,
213
+ });
214
+ }
215
+
216
+ /**
217
+ * Adds the policy constructor arguments and returns the code that builds the
218
+ * policy map passed to `add_context_rule`.
219
+ */
220
+ function addPolicy(c: ContractBuilder, opts: Required<AccountOptions>, signerArgs: string[]): string[] {
221
+ if (opts.policy === false) {
222
+ return ['let policies: Map<Address, Val> = Map::new(e);'];
223
+ }
224
+
225
+ c.addUseClause('soroban_sdk', 'IntoVal');
226
+
227
+ const lines: string[] = [];
228
+ let installParam: string;
229
+
230
+ if (opts.policy === 'weighted-threshold') {
231
+ c.addUseClause('soroban_sdk', 'panic_with_error');
232
+ c.addUseClause('stellar_accounts::policies::weighted_threshold', 'WeightedThresholdAccountParams');
233
+ c.addError('WeightsLengthMismatch', WEIGHTS_LENGTH_MISMATCH_ERROR);
234
+ // A flat vector rather than a `Map<Signer, u32>` so that callers never have
235
+ // to spell out `Signer` values, which is the point of the typed
236
+ // per-signer-type arguments. The assembly order fixes the signer indices,
237
+ // so whoever deploys the contract needs to know it.
238
+ c.addConstructorArgument({
239
+ name: 'weights',
240
+ type: 'Vec<u32>',
241
+ comment:
242
+ signerArgs.length === 1
243
+ ? [`One weight per signer, positionally aligned with \`${signerArgs[0]}\`.`]
244
+ : [
245
+ 'One weight per signer, positionally aligned with the signers assembled',
246
+ `from ${signerArgs.join(', ')}, in that order.`,
247
+ ],
248
+ });
249
+
250
+ lines.push(
251
+ 'if weights.len() != signers.len() {',
252
+ ` panic_with_error!(e, ${c.name}Error::WeightsLengthMismatch);`,
253
+ '}',
254
+ '',
255
+ 'let mut signer_weights: Map<Signer, u32> = Map::new(e);',
256
+ 'for (i, signer) in signers.iter().enumerate() {',
257
+ ' signer_weights.set(signer, weights.get_unchecked(i as u32));',
258
+ '}',
259
+ '',
260
+ );
261
+ installParam = 'WeightedThresholdAccountParams { signer_weights, threshold }';
262
+ } else {
263
+ c.addUseClause('stellar_accounts::policies::simple_threshold', 'SimpleThresholdAccountParams');
264
+ installParam = 'SimpleThresholdAccountParams { threshold }';
265
+ }
266
+
267
+ // The threshold is a deployment argument because it is only valid relative to
268
+ // the number of signers, which is not known when the contract is generated.
269
+ c.addConstructorArgument({ name: 'threshold', type: 'u32' });
270
+ c.addConstructorArgument({
271
+ name: 'threshold_policy',
272
+ type: 'Address',
273
+ comment: [
274
+ `${opts.policy === 'weighted-threshold' ? 'Weighted threshold' : 'Simple threshold'} policy contract.`,
275
+ ...REGISTRY_HINT,
276
+ ],
277
+ });
278
+
279
+ lines.push(
280
+ 'let mut policies: Map<Address, Val> = Map::new(e);',
281
+ 'policies.set(',
282
+ ' threshold_policy,',
283
+ ` ${installParam}.into_val(e),`,
284
+ ');',
285
+ );
286
+
287
+ return lines;
288
+ }
289
+
290
+ // Direct contract-to-contract calls are always authorized in Soroban, so an
291
+ // account needs this entry point to reach contracts it owns, such as its own
292
+ // policies, without the call re-entering `__check_auth`.
293
+ function addExecutionEntryPoint(c: ContractBuilder) {
294
+ c.addUseClause('stellar_accounts::smart_account', 'ExecutionEntryPoint');
295
+ // `execute` takes the target function name as a `Symbol`, which the
296
+ // contracttrait macro needs in scope.
297
+ c.addUseClause('soroban_sdk', 'Symbol');
298
+
299
+ c.addTraitImplBlock({
300
+ traitName: 'ExecutionEntryPoint',
301
+ structName: c.name,
302
+ tags: ['contractimpl(contracttrait)'],
303
+ priority: 2,
304
+ });
305
+ }
306
+
307
+ export const functions = defineFunctions({
308
+ // CustomAccountInterface Trait
309
+ __check_auth: {
310
+ args: [
311
+ { name: 'e', type: 'Env' },
312
+ { name: 'signature_payload', type: 'Hash<32>' },
313
+ { name: 'signatures', type: 'AuthPayload' },
314
+ { name: 'auth_contexts', type: 'Vec<Context>' },
315
+ ],
316
+ returns: 'Result<(), Self::Error>',
317
+ code: ['smart_account::do_check_auth(&e, &signature_payload, &signatures, &auth_contexts)'],
318
+ },
319
+ });
@@ -4,6 +4,35 @@ import { requireAccessControl } from './set-access-control';
4
4
  import type { BaseFunction, ContractBuilder } from './contract';
5
5
  import { defineFunctions } from './utils/define-functions';
6
6
 
7
+ /**
8
+ * Makes the contract upgradeable, authorized by the contract itself rather than
9
+ * by an access control role.
10
+ *
11
+ * Used by smart accounts, where authorization always flows through
12
+ * `__check_auth` and the account's context rules: an `Ownable`/`AccessControl`
13
+ * gate would add a second, weaker authorization path.
14
+ */
15
+ export function addSelfAuthUpgradeable(c: ContractBuilder) {
16
+ c.addUseClause('soroban_sdk', 'Address');
17
+ c.addUseClause('soroban_sdk', 'BytesN');
18
+ c.addUseClause('stellar_contract_utils::upgradeable', 'self', { alias: 'upgradeable' });
19
+ c.addUseClause('stellar_contract_utils::upgradeable', 'Upgradeable');
20
+
21
+ c.addTraitFunction(
22
+ {
23
+ traitName: 'Upgradeable',
24
+ structName: c.name,
25
+ tags: ['contractimpl'],
26
+ section: 'Utils',
27
+ },
28
+ {
29
+ name: 'upgrade',
30
+ args: [getSelfArg(), { name: 'new_wasm_hash', type: 'BytesN<32>' }, { name: '_operator', type: 'Address' }],
31
+ code: ['e.current_contract_address().require_auth()', 'upgradeable::upgrade(e, &new_wasm_hash)'],
32
+ },
33
+ );
34
+ }
35
+
7
36
  export function addUpgradeable(c: ContractBuilder, access: Access, explicitImplementations: boolean) {
8
37
  const functions = defineFunctions({
9
38
  upgrade: {
package/src/api.ts CHANGED
@@ -1,4 +1,5 @@
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';
@@ -29,6 +30,7 @@ import {
29
30
  defaults as nonFungibledefaults,
30
31
  isAccessControlRequired as nonFungibleIsAccessControlRequired,
31
32
  } from './non-fungible';
33
+ import { printAccount, defaults as accountDefaults } from './account';
32
34
 
33
35
  export interface WizardContractAPI<Options extends CommonOptions> {
34
36
  /**
@@ -50,12 +52,22 @@ export interface AccessControlAPI<Options extends CommonContractOptions> {
50
52
  isAccessControlRequired: (opts: Partial<Options>) => boolean;
51
53
  }
52
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>;
53
60
  export type Fungible = WizardContractAPI<FungibleOptions> & AccessControlAPI<FungibleOptions>;
54
61
  export type Governor = WizardContractAPI<GovernorOptions> & AccessControlAPI<GovernorOptions>;
55
62
  export type NonFungible = WizardContractAPI<NonFungibleOptions> & AccessControlAPI<NonFungibleOptions>;
56
63
  export type Stablecoin = WizardContractAPI<StablecoinOptions> & AccessControlAPI<StablecoinOptions>;
57
64
  export type Vault = WizardContractAPI<VaultOptions> & AccessControlAPI<VaultOptions>;
58
65
 
66
+ export const account: Account = {
67
+ print: printAccount,
68
+ defaults: accountDefaults,
69
+ };
70
+
59
71
  export const fungible: Fungible = {
60
72
  print: printFungible,
61
73
  defaults: fungibledefaults,
@@ -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';
@@ -10,6 +12,7 @@ import type { VaultOptions } from './vault';
10
12
  import { buildVault } from './vault';
11
13
 
12
14
  export interface KindedOptions {
15
+ Account: { kind: 'Account' } & AccountOptions;
13
16
  Fungible: { kind: 'Fungible' } & FungibleOptions;
14
17
  Governor: { kind: 'Governor' } & GovernorOptions;
15
18
  NonFungible: { kind: 'NonFungible' } & NonFungibleOptions;
@@ -21,6 +24,9 @@ export type GenericOptions = KindedOptions[keyof KindedOptions];
21
24
 
22
25
  export function buildGeneric(opts: GenericOptions) {
23
26
  switch (opts.kind) {
27
+ case 'Account':
28
+ return buildAccount(opts);
29
+
24
30
  case 'Fungible':
25
31
  return buildFungible(opts);
26
32