@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.
- package/README.md +4 -0
- package/dist/account.d.ts +19 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +251 -0
- package/dist/account.js.map +1 -0
- package/dist/add-upgradeable.d.ts +9 -0
- package/dist/add-upgradeable.d.ts.map +1 -1
- package/dist/add-upgradeable.js +25 -0
- package/dist/add-upgradeable.js.map +1 -1
- package/dist/api.d.ts +10 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +12 -1
- package/dist/api.js.map +1 -1
- package/dist/build-generic.d.ts +8 -0
- package/dist/build-generic.d.ts.map +1 -1
- package/dist/build-generic.js +6 -0
- package/dist/build-generic.js.map +1 -1
- package/dist/contract.d.ts +21 -2
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +11 -0
- package/dist/contract.js.map +1 -1
- package/dist/generate/account.d.ts +3 -0
- package/dist/generate/account.d.ts.map +1 -0
- package/dist/generate/account.js +21 -0
- package/dist/generate/account.js.map +1 -0
- package/dist/generate/sources.d.ts.map +1 -1
- package/dist/generate/sources.js +12 -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 +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/kind.js +2 -0
- package/dist/kind.js.map +1 -1
- package/dist/print.js +39 -9
- package/dist/print.js.map +1 -1
- package/dist/utils/compile-test.d.ts.map +1 -1
- package/dist/utils/compile-test.js +5 -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 +14 -1
- package/dist/zip-rust.js.map +1 -1
- package/dist/zip-shared.d.ts +15 -0
- package/dist/zip-shared.d.ts.map +1 -1
- package/dist/zip-shared.js +271 -1
- package/dist/zip-shared.js.map +1 -1
- package/package.json +1 -1
- package/src/account.ts +319 -0
- package/src/add-upgradeable.ts +29 -0
- package/src/api.ts +25 -0
- package/src/build-generic.ts +12 -0
- package/src/contract.ts +24 -2
- package/src/generate/account.ts +21 -0
- package/src/generate/sources.ts +14 -0
- package/src/generate/vault.ts +21 -0
- package/src/index.ts +4 -0
- package/src/kind.ts +2 -0
- package/src/print.ts +47 -18
- package/src/utils/compile-test.ts +6 -1
- package/src/vault.ts +378 -0
- package/src/zip-rust.ts +17 -1
- 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',
|