@aztec/test-wallet 3.0.0-nightly.20250911
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/dest/index.d.ts +4 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +2 -0
- package/dest/lazy.d.ts +4 -0
- package/dest/lazy.d.ts.map +1 -0
- package/dest/lazy.js +2 -0
- package/dest/utils.d.ts +9 -0
- package/dest/utils.d.ts.map +1 -0
- package/dest/utils.js +16 -0
- package/dest/wallet/bundle.d.ts +16 -0
- package/dest/wallet/bundle.d.ts.map +1 -0
- package/dest/wallet/bundle.js +51 -0
- package/dest/wallet/lazy.d.ts +16 -0
- package/dest/wallet/lazy.d.ts.map +1 -0
- package/dest/wallet/lazy.js +52 -0
- package/dest/wallet/test_wallet.d.ts +51 -0
- package/dest/wallet/test_wallet.d.ts.map +1 -0
- package/dest/wallet/test_wallet.js +63 -0
- package/package.json +92 -0
- package/src/index.ts +3 -0
- package/src/lazy.ts +3 -0
- package/src/utils.ts +28 -0
- package/src/wallet/bundle.ts +57 -0
- package/src/wallet/lazy.ts +58 -0
- package/src/wallet/test_wallet.ts +119 -0
package/dest/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC"}
|
package/dest/index.js
ADDED
package/dest/lazy.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../src/lazy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC"}
|
package/dest/lazy.js
ADDED
package/dest/utils.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InitialAccountData } from '@aztec/accounts/testing';
|
|
2
|
+
import type { WaitOpts } from '@aztec/aztec.js';
|
|
3
|
+
import type { BaseTestWallet } from './wallet/test_wallet.js';
|
|
4
|
+
/**
|
|
5
|
+
* Deploys the SchnorrAccount contracts backed by prefunded addresses
|
|
6
|
+
* at genesis. This can be directly used to pay for transactions in FeeJuice.
|
|
7
|
+
*/
|
|
8
|
+
export declare function deployFundedSchnorrAccounts(wallet: BaseTestWallet, accountsData: InitialAccountData[], waitOptions?: WaitOpts): Promise<import("@aztec/aztec.js").AccountManager[]>;
|
|
9
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D;;;GAGG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,cAAc,EACtB,YAAY,EAAE,kBAAkB,EAAE,EAClC,WAAW,CAAC,EAAE,QAAQ,uDAevB"}
|
package/dest/utils.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploys the SchnorrAccount contracts backed by prefunded addresses
|
|
3
|
+
* at genesis. This can be directly used to pay for transactions in FeeJuice.
|
|
4
|
+
*/ export async function deployFundedSchnorrAccounts(wallet, accountsData, waitOptions) {
|
|
5
|
+
const accountManagers = [];
|
|
6
|
+
// Serial due to https://github.com/AztecProtocol/aztec-packages/issues/12045
|
|
7
|
+
for(let i = 0; i < accountsData.length; i++){
|
|
8
|
+
const { secret, salt, signingKey } = accountsData[i];
|
|
9
|
+
const accountManager = await wallet.createSchnorrAccount(secret, salt, signingKey);
|
|
10
|
+
await accountManager.deploy({
|
|
11
|
+
skipClassPublication: i !== 0
|
|
12
|
+
}).wait(waitOptions);
|
|
13
|
+
accountManagers.push(accountManager);
|
|
14
|
+
}
|
|
15
|
+
return accountManagers;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AccountManager, type AztecAddress, Fq, Fr } from '@aztec/aztec.js';
|
|
2
|
+
import { BaseTestWallet } from './test_wallet.js';
|
|
3
|
+
/**
|
|
4
|
+
* A TestWallet implementation that loads the account contract artifacts eagerly
|
|
5
|
+
*/
|
|
6
|
+
export declare class TestWallet extends BaseTestWallet {
|
|
7
|
+
createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq): Promise<AccountManager>;
|
|
8
|
+
createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
9
|
+
createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
10
|
+
getFakeAccountDataFor(address: AztecAddress): Promise<{
|
|
11
|
+
account: import("@aztec/aztec.js").BaseAccount;
|
|
12
|
+
instance: import("@aztec/aztec.js").ContractInstanceWithAddress;
|
|
13
|
+
artifact: import("@aztec/aztec.js").ContractArtifact;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/wallet/bundle.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,EAAE,EAAE,EAAE,EAA8C,MAAM,iBAAiB,CAAC;AAGxH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;GAEG;AACH,qBAAa,UAAW,SAAQ,cAAc;IAC5C,oBAAoB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAUpF,mBAAmB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAStF,mBAAmB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAShF,qBAAqB,CAAC,OAAO,EAAE,YAAY;;;;;CAgBlD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { EcdsaKAccountContract, EcdsaRAccountContract } from '@aztec/accounts/ecdsa';
|
|
2
|
+
import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
|
|
3
|
+
import { StubAccountContractArtifact, createStubAccount } from '@aztec/accounts/stub';
|
|
4
|
+
import { getContractInstanceFromInstantiationParams } from '@aztec/aztec.js';
|
|
5
|
+
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
6
|
+
import { BaseTestWallet } from './test_wallet.js';
|
|
7
|
+
/**
|
|
8
|
+
* A TestWallet implementation that loads the account contract artifacts eagerly
|
|
9
|
+
*/ export class TestWallet extends BaseTestWallet {
|
|
10
|
+
createSchnorrAccount(secret, salt, signingKey) {
|
|
11
|
+
signingKey = signingKey ?? deriveSigningKey(secret);
|
|
12
|
+
const accountData = {
|
|
13
|
+
secret,
|
|
14
|
+
salt,
|
|
15
|
+
contract: new SchnorrAccountContract(signingKey)
|
|
16
|
+
};
|
|
17
|
+
return this.createAccount(accountData);
|
|
18
|
+
}
|
|
19
|
+
createECDSARAccount(secret, salt, signingKey) {
|
|
20
|
+
const accountData = {
|
|
21
|
+
secret,
|
|
22
|
+
salt,
|
|
23
|
+
contract: new EcdsaRAccountContract(signingKey)
|
|
24
|
+
};
|
|
25
|
+
return this.createAccount(accountData);
|
|
26
|
+
}
|
|
27
|
+
createECDSAKAccount(secret, salt, signingKey) {
|
|
28
|
+
const accountData = {
|
|
29
|
+
secret,
|
|
30
|
+
salt,
|
|
31
|
+
contract: new EcdsaKAccountContract(signingKey)
|
|
32
|
+
};
|
|
33
|
+
return this.createAccount(accountData);
|
|
34
|
+
}
|
|
35
|
+
async getFakeAccountDataFor(address) {
|
|
36
|
+
const nodeInfo = await this.pxe.getNodeInfo();
|
|
37
|
+
const originalAccount = await this.getAccountFromAddress(address);
|
|
38
|
+
const originalAddress = originalAccount.getCompleteAddress();
|
|
39
|
+
const { contractInstance } = await this.pxe.getContractMetadata(originalAddress.address);
|
|
40
|
+
if (!contractInstance) {
|
|
41
|
+
throw new Error(`No contract instance found for address: ${originalAddress.address}`);
|
|
42
|
+
}
|
|
43
|
+
const stubAccount = createStubAccount(originalAddress, nodeInfo);
|
|
44
|
+
const instance = await getContractInstanceFromInstantiationParams(StubAccountContractArtifact, {});
|
|
45
|
+
return {
|
|
46
|
+
account: stubAccount,
|
|
47
|
+
instance,
|
|
48
|
+
artifact: StubAccountContractArtifact
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AccountManager, type AztecAddress, Fq, Fr } from '@aztec/aztec.js';
|
|
2
|
+
import { BaseTestWallet } from './test_wallet.js';
|
|
3
|
+
/**
|
|
4
|
+
* A TestWallet implementation that loads the account contract artifacts lazily
|
|
5
|
+
*/
|
|
6
|
+
export declare class TestWallet extends BaseTestWallet {
|
|
7
|
+
createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq): Promise<AccountManager>;
|
|
8
|
+
createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
9
|
+
createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
10
|
+
getFakeAccountDataFor(address: AztecAddress): Promise<{
|
|
11
|
+
account: import("@aztec/aztec.js").BaseAccount;
|
|
12
|
+
instance: import("@aztec/aztec.js").ContractInstanceWithAddress;
|
|
13
|
+
artifact: import("@aztec/aztec.js").ContractArtifact;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=lazy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../src/wallet/lazy.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,EAAE,EAAE,EAAE,EAA8C,MAAM,iBAAiB,CAAC;AAGxH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;GAEG;AACH,qBAAa,UAAW,SAAQ,cAAc;IAC5C,oBAAoB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAUpF,mBAAmB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAStF,mBAAmB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAShF,qBAAqB,CAAC,OAAO,EAAE,YAAY;;;;;CAiBlD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { EcdsaKAccountContract, EcdsaRAccountContract } from '@aztec/accounts/ecdsa/lazy';
|
|
2
|
+
import { SchnorrAccountContract } from '@aztec/accounts/schnorr/lazy';
|
|
3
|
+
import { createStubAccount, getStubAccountContractArtifact } from '@aztec/accounts/stub/lazy';
|
|
4
|
+
import { getContractInstanceFromInstantiationParams } from '@aztec/aztec.js';
|
|
5
|
+
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
6
|
+
import { BaseTestWallet } from './test_wallet.js';
|
|
7
|
+
/**
|
|
8
|
+
* A TestWallet implementation that loads the account contract artifacts lazily
|
|
9
|
+
*/ export class TestWallet extends BaseTestWallet {
|
|
10
|
+
createSchnorrAccount(secret, salt, signingKey) {
|
|
11
|
+
signingKey = signingKey ?? deriveSigningKey(secret);
|
|
12
|
+
const accountData = {
|
|
13
|
+
secret,
|
|
14
|
+
salt,
|
|
15
|
+
contract: new SchnorrAccountContract(signingKey)
|
|
16
|
+
};
|
|
17
|
+
return this.createAccount(accountData);
|
|
18
|
+
}
|
|
19
|
+
createECDSARAccount(secret, salt, signingKey) {
|
|
20
|
+
const accountData = {
|
|
21
|
+
secret,
|
|
22
|
+
salt,
|
|
23
|
+
contract: new EcdsaRAccountContract(signingKey)
|
|
24
|
+
};
|
|
25
|
+
return this.createAccount(accountData);
|
|
26
|
+
}
|
|
27
|
+
createECDSAKAccount(secret, salt, signingKey) {
|
|
28
|
+
const accountData = {
|
|
29
|
+
secret,
|
|
30
|
+
salt,
|
|
31
|
+
contract: new EcdsaKAccountContract(signingKey)
|
|
32
|
+
};
|
|
33
|
+
return this.createAccount(accountData);
|
|
34
|
+
}
|
|
35
|
+
async getFakeAccountDataFor(address) {
|
|
36
|
+
const nodeInfo = await this.pxe.getNodeInfo();
|
|
37
|
+
const originalAccount = await this.getAccountFromAddress(address);
|
|
38
|
+
const originalAddress = originalAccount.getCompleteAddress();
|
|
39
|
+
const { contractInstance } = await this.pxe.getContractMetadata(originalAddress.address);
|
|
40
|
+
if (!contractInstance) {
|
|
41
|
+
throw new Error(`No contract instance found for address: ${originalAddress.address}`);
|
|
42
|
+
}
|
|
43
|
+
const stubAccount = createStubAccount(originalAddress, nodeInfo);
|
|
44
|
+
const StubAccountContractArtifact = await getStubAccountContractArtifact();
|
|
45
|
+
const instance = await getContractInstanceFromInstantiationParams(StubAccountContractArtifact, {});
|
|
46
|
+
return {
|
|
47
|
+
account: stubAccount,
|
|
48
|
+
instance,
|
|
49
|
+
artifact: StubAccountContractArtifact
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Account, type AccountContract, AccountManager, BaseWallet, type ContractArtifact, type IntentAction, type IntentInnerHash, type SimulateMethodOptions } from '@aztec/aztec.js';
|
|
2
|
+
import type { ExecutionPayload } from '@aztec/entrypoints/payload';
|
|
3
|
+
import { Fq, Fr } from '@aztec/foundation/fields';
|
|
4
|
+
import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
5
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
+
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
7
|
+
import type { TxSimulationResult } from '@aztec/stdlib/tx';
|
|
8
|
+
/**
|
|
9
|
+
* Data for generating an account.
|
|
10
|
+
*/
|
|
11
|
+
export interface AccountData {
|
|
12
|
+
/**
|
|
13
|
+
* Secret to derive the keys for the account.
|
|
14
|
+
*/
|
|
15
|
+
secret: Fr;
|
|
16
|
+
/**
|
|
17
|
+
* Contract address salt.
|
|
18
|
+
*/
|
|
19
|
+
salt: Fr;
|
|
20
|
+
/**
|
|
21
|
+
* Contract that backs the account.
|
|
22
|
+
*/
|
|
23
|
+
contract: AccountContract;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Wallet implementation that stores accounts in memory and allows allows their creation
|
|
27
|
+
* from the outside (which is something actual wallets shouldn't allow!)
|
|
28
|
+
* It is intended to be used in e2e tests.
|
|
29
|
+
*/
|
|
30
|
+
export declare abstract class BaseTestWallet extends BaseWallet {
|
|
31
|
+
protected accounts: Map<string, Account>;
|
|
32
|
+
private simulatedSimulations;
|
|
33
|
+
enableSimulatedSimulations(): void;
|
|
34
|
+
disableSimulatedSimulations(): void;
|
|
35
|
+
protected getAccountFromAddress(address: AztecAddress): Promise<Account>;
|
|
36
|
+
createAccount(accountData: AccountData): Promise<AccountManager>;
|
|
37
|
+
abstract createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq): Promise<AccountManager>;
|
|
38
|
+
abstract createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
39
|
+
abstract createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
40
|
+
lookupValidity(address: AztecAddress, intent: IntentInnerHash | IntentAction, witness: AuthWitness): Promise<{
|
|
41
|
+
isValidInPrivate: boolean;
|
|
42
|
+
isValidInPublic: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
abstract getFakeAccountDataFor(address: AztecAddress): Promise<{
|
|
45
|
+
account: Account;
|
|
46
|
+
instance: ContractInstanceWithAddress;
|
|
47
|
+
artifact: ContractArtifact;
|
|
48
|
+
}>;
|
|
49
|
+
simulateTx(executionPayload: ExecutionPayload, opts: SimulateMethodOptions): Promise<TxSimulationResult>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=test_wallet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test_wallet.d.ts","sourceRoot":"","sources":["../../src/wallet/test_wallet.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,cAAc,EAEd,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,eAAe,EAEpB,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,MAAM,EAAE,EAAE,CAAC;IACX;;OAEG;IACH,IAAI,EAAE,EAAE,CAAC;IACT;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;;;GAIG;AACH,8BAAsB,cAAe,SAAQ,UAAU;IACrD,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAa;IAErD,OAAO,CAAC,oBAAoB,CAAS;IAErC,0BAA0B;IAI1B,2BAA2B;cAIX,qBAAqB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBxE,aAAa,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAgBtE,QAAQ,CAAC,oBAAoB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAC7F,QAAQ,CAAC,mBAAmB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAC/F,QAAQ,CAAC,mBAAmB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAEzF,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,YAAY,EAAE,OAAO,EAAE,WAAW;;;;IAKxG,QAAQ,CAAC,qBAAqB,CAC5B,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,2BAA2B,CAAC;QAAC,QAAQ,EAAE,gBAAgB,CAAA;KAAE,CAAC;IAEpF,UAAU,CACvB,gBAAgB,EAAE,gBAAgB,EAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,kBAAkB,CAAC;CAc/B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AccountManager, BaseWallet, SignerlessAccount } from '@aztec/aztec.js';
|
|
2
|
+
import { DefaultMultiCallEntrypoint } from '@aztec/entrypoints/multicall';
|
|
3
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
4
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
+
/**
|
|
6
|
+
* Wallet implementation that stores accounts in memory and allows allows their creation
|
|
7
|
+
* from the outside (which is something actual wallets shouldn't allow!)
|
|
8
|
+
* It is intended to be used in e2e tests.
|
|
9
|
+
*/ export class BaseTestWallet extends BaseWallet {
|
|
10
|
+
accounts = new Map();
|
|
11
|
+
simulatedSimulations = false;
|
|
12
|
+
enableSimulatedSimulations() {
|
|
13
|
+
this.simulatedSimulations = true;
|
|
14
|
+
}
|
|
15
|
+
disableSimulatedSimulations() {
|
|
16
|
+
this.simulatedSimulations = false;
|
|
17
|
+
}
|
|
18
|
+
async getAccountFromAddress(address) {
|
|
19
|
+
let account;
|
|
20
|
+
if (address.equals(AztecAddress.ZERO)) {
|
|
21
|
+
const { l1ChainId: chainId, rollupVersion } = await this.pxe.getNodeInfo();
|
|
22
|
+
account = new SignerlessAccount(new DefaultMultiCallEntrypoint(chainId, rollupVersion));
|
|
23
|
+
} else {
|
|
24
|
+
account = this.accounts.get(address?.toString() ?? '');
|
|
25
|
+
}
|
|
26
|
+
if (!account) {
|
|
27
|
+
throw new Error(`Account not found in wallet for address: ${address}`);
|
|
28
|
+
}
|
|
29
|
+
return account;
|
|
30
|
+
}
|
|
31
|
+
async createAccount(accountData) {
|
|
32
|
+
const accountManager = await AccountManager.create(this, this.pxe, accountData.secret, accountData.contract, accountData.salt);
|
|
33
|
+
await accountManager.register();
|
|
34
|
+
this.accounts.set(accountManager.getAddress().toString(), await accountManager.getAccount());
|
|
35
|
+
return accountManager;
|
|
36
|
+
}
|
|
37
|
+
async lookupValidity(address, intent, witness) {
|
|
38
|
+
const account = await this.getAccountFromAddress(address);
|
|
39
|
+
return account.lookupValidity(this, address, intent, witness);
|
|
40
|
+
}
|
|
41
|
+
async simulateTx(executionPayload, opts) {
|
|
42
|
+
if (!this.simulatedSimulations) {
|
|
43
|
+
return super.simulateTx(executionPayload, opts);
|
|
44
|
+
} else {
|
|
45
|
+
const executionOptions = {
|
|
46
|
+
txNonce: Fr.random(),
|
|
47
|
+
cancellable: false
|
|
48
|
+
};
|
|
49
|
+
const { account: fromAccount, instance, artifact } = await this.getFakeAccountDataFor(opts.from);
|
|
50
|
+
const fee = await this.getFeeOptions(fromAccount, executionPayload, opts.fee, executionOptions);
|
|
51
|
+
const txRequest = await fromAccount.createTxExecutionRequest(executionPayload, fee, executionOptions);
|
|
52
|
+
const contractOverrides = {
|
|
53
|
+
[opts.from.toString()]: {
|
|
54
|
+
instance,
|
|
55
|
+
artifact
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return this.pxe.simulateTx(txRequest, true, true, true, {
|
|
59
|
+
contracts: contractOverrides
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aztec/test-wallet",
|
|
3
|
+
"homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/test-wallet",
|
|
4
|
+
"version": "3.0.0-nightly.20250911",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./dest/index.js",
|
|
8
|
+
"./lazy": "./dest/lazy.js"
|
|
9
|
+
},
|
|
10
|
+
"typedocOptions": {
|
|
11
|
+
"entryPoints": [
|
|
12
|
+
"./src/index.ts"
|
|
13
|
+
],
|
|
14
|
+
"tsconfig": "./tsconfig.json"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "yarn clean && tsc -b",
|
|
18
|
+
"build:dev": "tsc -b --watch",
|
|
19
|
+
"build:ts": "tsc -b",
|
|
20
|
+
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
21
|
+
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
|
|
22
|
+
},
|
|
23
|
+
"inherits": [
|
|
24
|
+
"../package.common.json"
|
|
25
|
+
],
|
|
26
|
+
"jest": {
|
|
27
|
+
"moduleNameMapper": {
|
|
28
|
+
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
|
|
29
|
+
},
|
|
30
|
+
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
|
|
31
|
+
"rootDir": "./src",
|
|
32
|
+
"transform": {
|
|
33
|
+
"^.+\\.tsx?$": [
|
|
34
|
+
"@swc/jest",
|
|
35
|
+
{
|
|
36
|
+
"jsc": {
|
|
37
|
+
"parser": {
|
|
38
|
+
"syntax": "typescript",
|
|
39
|
+
"decorators": true
|
|
40
|
+
},
|
|
41
|
+
"transform": {
|
|
42
|
+
"decoratorVersion": "2022-03"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"extensionsToTreatAsEsm": [
|
|
49
|
+
".ts"
|
|
50
|
+
],
|
|
51
|
+
"reporters": [
|
|
52
|
+
"default"
|
|
53
|
+
],
|
|
54
|
+
"testTimeout": 120000,
|
|
55
|
+
"setupFiles": [
|
|
56
|
+
"../../foundation/src/jest/setup.mjs"
|
|
57
|
+
],
|
|
58
|
+
"testEnvironment": "../../foundation/src/jest/env.mjs",
|
|
59
|
+
"setupFilesAfterEnv": [
|
|
60
|
+
"../../foundation/src/jest/setupAfterEnv.mjs"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@aztec/accounts": "3.0.0-nightly.20250911",
|
|
65
|
+
"@aztec/aztec.js": "3.0.0-nightly.20250911",
|
|
66
|
+
"@aztec/entrypoints": "3.0.0-nightly.20250911",
|
|
67
|
+
"@aztec/foundation": "3.0.0-nightly.20250911",
|
|
68
|
+
"@aztec/noir-contracts.js": "3.0.0-nightly.20250911",
|
|
69
|
+
"@aztec/stdlib": "3.0.0-nightly.20250911"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@jest/globals": "^30.0.0",
|
|
73
|
+
"@types/jest": "^30.0.0",
|
|
74
|
+
"@types/node": "^22.15.17",
|
|
75
|
+
"jest": "^30.0.0",
|
|
76
|
+
"resolve-typescript-plugin": "^2.0.1",
|
|
77
|
+
"ts-loader": "^9.4.4",
|
|
78
|
+
"ts-node": "^10.9.1",
|
|
79
|
+
"typescript": "^5.3.3",
|
|
80
|
+
"util": "^0.12.5"
|
|
81
|
+
},
|
|
82
|
+
"files": [
|
|
83
|
+
"dest",
|
|
84
|
+
"src",
|
|
85
|
+
"!*.test.*",
|
|
86
|
+
"artifacts"
|
|
87
|
+
],
|
|
88
|
+
"types": "./dest/index.d.ts",
|
|
89
|
+
"engines": {
|
|
90
|
+
"node": ">=20.10"
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/index.ts
ADDED
package/src/lazy.ts
ADDED
package/src/utils.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { InitialAccountData } from '@aztec/accounts/testing';
|
|
2
|
+
import type { WaitOpts } from '@aztec/aztec.js';
|
|
3
|
+
|
|
4
|
+
import type { BaseTestWallet } from './wallet/test_wallet.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deploys the SchnorrAccount contracts backed by prefunded addresses
|
|
8
|
+
* at genesis. This can be directly used to pay for transactions in FeeJuice.
|
|
9
|
+
*/
|
|
10
|
+
export async function deployFundedSchnorrAccounts(
|
|
11
|
+
wallet: BaseTestWallet,
|
|
12
|
+
accountsData: InitialAccountData[],
|
|
13
|
+
waitOptions?: WaitOpts,
|
|
14
|
+
) {
|
|
15
|
+
const accountManagers = [];
|
|
16
|
+
// Serial due to https://github.com/AztecProtocol/aztec-packages/issues/12045
|
|
17
|
+
for (let i = 0; i < accountsData.length; i++) {
|
|
18
|
+
const { secret, salt, signingKey } = accountsData[i];
|
|
19
|
+
const accountManager = await wallet.createSchnorrAccount(secret, salt, signingKey);
|
|
20
|
+
await accountManager
|
|
21
|
+
.deploy({
|
|
22
|
+
skipClassPublication: i !== 0, // Publish the contract class at most once.
|
|
23
|
+
})
|
|
24
|
+
.wait(waitOptions);
|
|
25
|
+
accountManagers.push(accountManager);
|
|
26
|
+
}
|
|
27
|
+
return accountManagers;
|
|
28
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { EcdsaKAccountContract, EcdsaRAccountContract } from '@aztec/accounts/ecdsa';
|
|
2
|
+
import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
|
|
3
|
+
import { StubAccountContractArtifact, createStubAccount } from '@aztec/accounts/stub';
|
|
4
|
+
import { AccountManager, type AztecAddress, Fq, Fr, getContractInstanceFromInstantiationParams } from '@aztec/aztec.js';
|
|
5
|
+
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
6
|
+
|
|
7
|
+
import { BaseTestWallet } from './test_wallet.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A TestWallet implementation that loads the account contract artifacts eagerly
|
|
11
|
+
*/
|
|
12
|
+
export class TestWallet extends BaseTestWallet {
|
|
13
|
+
createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq): Promise<AccountManager> {
|
|
14
|
+
signingKey = signingKey ?? deriveSigningKey(secret);
|
|
15
|
+
const accountData = {
|
|
16
|
+
secret,
|
|
17
|
+
salt,
|
|
18
|
+
contract: new SchnorrAccountContract(signingKey),
|
|
19
|
+
};
|
|
20
|
+
return this.createAccount(accountData);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager> {
|
|
24
|
+
const accountData = {
|
|
25
|
+
secret,
|
|
26
|
+
salt,
|
|
27
|
+
contract: new EcdsaRAccountContract(signingKey),
|
|
28
|
+
};
|
|
29
|
+
return this.createAccount(accountData);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager> {
|
|
33
|
+
const accountData = {
|
|
34
|
+
secret,
|
|
35
|
+
salt,
|
|
36
|
+
contract: new EcdsaKAccountContract(signingKey),
|
|
37
|
+
};
|
|
38
|
+
return this.createAccount(accountData);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getFakeAccountDataFor(address: AztecAddress) {
|
|
42
|
+
const nodeInfo = await this.pxe.getNodeInfo();
|
|
43
|
+
const originalAccount = await this.getAccountFromAddress(address);
|
|
44
|
+
const originalAddress = originalAccount.getCompleteAddress();
|
|
45
|
+
const { contractInstance } = await this.pxe.getContractMetadata(originalAddress.address);
|
|
46
|
+
if (!contractInstance) {
|
|
47
|
+
throw new Error(`No contract instance found for address: ${originalAddress.address}`);
|
|
48
|
+
}
|
|
49
|
+
const stubAccount = createStubAccount(originalAddress, nodeInfo);
|
|
50
|
+
const instance = await getContractInstanceFromInstantiationParams(StubAccountContractArtifact, {});
|
|
51
|
+
return {
|
|
52
|
+
account: stubAccount,
|
|
53
|
+
instance,
|
|
54
|
+
artifact: StubAccountContractArtifact,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { EcdsaKAccountContract, EcdsaRAccountContract } from '@aztec/accounts/ecdsa/lazy';
|
|
2
|
+
import { SchnorrAccountContract } from '@aztec/accounts/schnorr/lazy';
|
|
3
|
+
import { createStubAccount, getStubAccountContractArtifact } from '@aztec/accounts/stub/lazy';
|
|
4
|
+
import { AccountManager, type AztecAddress, Fq, Fr, getContractInstanceFromInstantiationParams } from '@aztec/aztec.js';
|
|
5
|
+
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
6
|
+
|
|
7
|
+
import { BaseTestWallet } from './test_wallet.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A TestWallet implementation that loads the account contract artifacts lazily
|
|
11
|
+
*/
|
|
12
|
+
export class TestWallet extends BaseTestWallet {
|
|
13
|
+
createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq): Promise<AccountManager> {
|
|
14
|
+
signingKey = signingKey ?? deriveSigningKey(secret);
|
|
15
|
+
const accountData = {
|
|
16
|
+
secret,
|
|
17
|
+
salt,
|
|
18
|
+
contract: new SchnorrAccountContract(signingKey),
|
|
19
|
+
};
|
|
20
|
+
return this.createAccount(accountData);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager> {
|
|
24
|
+
const accountData = {
|
|
25
|
+
secret,
|
|
26
|
+
salt,
|
|
27
|
+
contract: new EcdsaRAccountContract(signingKey),
|
|
28
|
+
};
|
|
29
|
+
return this.createAccount(accountData);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager> {
|
|
33
|
+
const accountData = {
|
|
34
|
+
secret,
|
|
35
|
+
salt,
|
|
36
|
+
contract: new EcdsaKAccountContract(signingKey),
|
|
37
|
+
};
|
|
38
|
+
return this.createAccount(accountData);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getFakeAccountDataFor(address: AztecAddress) {
|
|
42
|
+
const nodeInfo = await this.pxe.getNodeInfo();
|
|
43
|
+
const originalAccount = await this.getAccountFromAddress(address);
|
|
44
|
+
const originalAddress = originalAccount.getCompleteAddress();
|
|
45
|
+
const { contractInstance } = await this.pxe.getContractMetadata(originalAddress.address);
|
|
46
|
+
if (!contractInstance) {
|
|
47
|
+
throw new Error(`No contract instance found for address: ${originalAddress.address}`);
|
|
48
|
+
}
|
|
49
|
+
const stubAccount = createStubAccount(originalAddress, nodeInfo);
|
|
50
|
+
const StubAccountContractArtifact = await getStubAccountContractArtifact();
|
|
51
|
+
const instance = await getContractInstanceFromInstantiationParams(StubAccountContractArtifact, {});
|
|
52
|
+
return {
|
|
53
|
+
account: stubAccount,
|
|
54
|
+
instance,
|
|
55
|
+
artifact: StubAccountContractArtifact,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Account,
|
|
3
|
+
type AccountContract,
|
|
4
|
+
AccountManager,
|
|
5
|
+
BaseAccount,
|
|
6
|
+
BaseWallet,
|
|
7
|
+
type ContractArtifact,
|
|
8
|
+
type IntentAction,
|
|
9
|
+
type IntentInnerHash,
|
|
10
|
+
SignerlessAccount,
|
|
11
|
+
type SimulateMethodOptions,
|
|
12
|
+
} from '@aztec/aztec.js';
|
|
13
|
+
import { DefaultMultiCallEntrypoint } from '@aztec/entrypoints/multicall';
|
|
14
|
+
import type { ExecutionPayload } from '@aztec/entrypoints/payload';
|
|
15
|
+
import { Fq, Fr } from '@aztec/foundation/fields';
|
|
16
|
+
import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
17
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
18
|
+
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
19
|
+
import type { TxSimulationResult } from '@aztec/stdlib/tx';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Data for generating an account.
|
|
23
|
+
*/
|
|
24
|
+
export interface AccountData {
|
|
25
|
+
/**
|
|
26
|
+
* Secret to derive the keys for the account.
|
|
27
|
+
*/
|
|
28
|
+
secret: Fr;
|
|
29
|
+
/**
|
|
30
|
+
* Contract address salt.
|
|
31
|
+
*/
|
|
32
|
+
salt: Fr;
|
|
33
|
+
/**
|
|
34
|
+
* Contract that backs the account.
|
|
35
|
+
*/
|
|
36
|
+
contract: AccountContract;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Wallet implementation that stores accounts in memory and allows allows their creation
|
|
41
|
+
* from the outside (which is something actual wallets shouldn't allow!)
|
|
42
|
+
* It is intended to be used in e2e tests.
|
|
43
|
+
*/
|
|
44
|
+
export abstract class BaseTestWallet extends BaseWallet {
|
|
45
|
+
protected accounts: Map<string, Account> = new Map();
|
|
46
|
+
|
|
47
|
+
private simulatedSimulations = false;
|
|
48
|
+
|
|
49
|
+
enableSimulatedSimulations() {
|
|
50
|
+
this.simulatedSimulations = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
disableSimulatedSimulations() {
|
|
54
|
+
this.simulatedSimulations = false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
protected async getAccountFromAddress(address: AztecAddress): Promise<Account> {
|
|
58
|
+
let account: Account | undefined;
|
|
59
|
+
if (address.equals(AztecAddress.ZERO)) {
|
|
60
|
+
const { l1ChainId: chainId, rollupVersion } = await this.pxe.getNodeInfo();
|
|
61
|
+
account = new SignerlessAccount(new DefaultMultiCallEntrypoint(chainId, rollupVersion));
|
|
62
|
+
} else {
|
|
63
|
+
account = this.accounts.get(address?.toString() ?? '');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!account) {
|
|
67
|
+
throw new Error(`Account not found in wallet for address: ${address}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return account;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async createAccount(accountData: AccountData): Promise<AccountManager> {
|
|
74
|
+
const accountManager = await AccountManager.create(
|
|
75
|
+
this,
|
|
76
|
+
this.pxe,
|
|
77
|
+
accountData.secret,
|
|
78
|
+
accountData.contract,
|
|
79
|
+
accountData.salt,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
await accountManager.register();
|
|
83
|
+
|
|
84
|
+
this.accounts.set(accountManager.getAddress().toString(), await accountManager.getAccount());
|
|
85
|
+
|
|
86
|
+
return accountManager;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
abstract createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq): Promise<AccountManager>;
|
|
90
|
+
abstract createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
91
|
+
abstract createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager>;
|
|
92
|
+
|
|
93
|
+
async lookupValidity(address: AztecAddress, intent: IntentInnerHash | IntentAction, witness: AuthWitness) {
|
|
94
|
+
const account = (await this.getAccountFromAddress(address)) as BaseAccount;
|
|
95
|
+
return account.lookupValidity(this, address, intent, witness);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
abstract getFakeAccountDataFor(
|
|
99
|
+
address: AztecAddress, // eslint-disable-next-line jsdoc/require-jsdoc
|
|
100
|
+
): Promise<{ account: Account; instance: ContractInstanceWithAddress; artifact: ContractArtifact }>;
|
|
101
|
+
|
|
102
|
+
override async simulateTx(
|
|
103
|
+
executionPayload: ExecutionPayload,
|
|
104
|
+
opts: SimulateMethodOptions,
|
|
105
|
+
): Promise<TxSimulationResult> {
|
|
106
|
+
if (!this.simulatedSimulations) {
|
|
107
|
+
return super.simulateTx(executionPayload, opts);
|
|
108
|
+
} else {
|
|
109
|
+
const executionOptions = { txNonce: Fr.random(), cancellable: false };
|
|
110
|
+
const { account: fromAccount, instance, artifact } = await this.getFakeAccountDataFor(opts.from);
|
|
111
|
+
const fee = await this.getFeeOptions(fromAccount, executionPayload, opts.fee, executionOptions);
|
|
112
|
+
const txRequest = await fromAccount.createTxExecutionRequest(executionPayload, fee, executionOptions);
|
|
113
|
+
const contractOverrides = {
|
|
114
|
+
[opts.from.toString()]: { instance, artifact },
|
|
115
|
+
};
|
|
116
|
+
return this.pxe.simulateTx(txRequest, true /* simulatePublic */, true, true, { contracts: contractOverrides });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|