@aztec/wallets 0.0.1-commit.033589e
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/embedded/account-contract-providers/bundle.d.ts +21 -0
- package/dest/embedded/account-contract-providers/bundle.d.ts.map +1 -0
- package/dest/embedded/account-contract-providers/bundle.js +27 -0
- package/dest/embedded/account-contract-providers/lazy.d.ts +21 -0
- package/dest/embedded/account-contract-providers/lazy.d.ts.map +1 -0
- package/dest/embedded/account-contract-providers/lazy.js +29 -0
- package/dest/embedded/account-contract-providers/types.d.ts +22 -0
- package/dest/embedded/account-contract-providers/types.d.ts.map +1 -0
- package/dest/embedded/account-contract-providers/types.js +6 -0
- package/dest/embedded/embedded_wallet.d.ts +47 -0
- package/dest/embedded/embedded_wallet.d.ts.map +1 -0
- package/dest/embedded/embedded_wallet.js +171 -0
- package/dest/embedded/entrypoints/browser.d.ts +14 -0
- package/dest/embedded/entrypoints/browser.d.ts.map +1 -0
- package/dest/embedded/entrypoints/browser.js +42 -0
- package/dest/embedded/entrypoints/node.d.ts +14 -0
- package/dest/embedded/entrypoints/node.d.ts.map +1 -0
- package/dest/embedded/entrypoints/node.js +42 -0
- package/dest/embedded/wallet_db.d.ts +34 -0
- package/dest/embedded/wallet_db.d.ts.map +1 -0
- package/dest/embedded/wallet_db.js +120 -0
- package/dest/testing.d.ts +12 -0
- package/dest/testing.d.ts.map +1 -0
- package/dest/testing.js +24 -0
- package/package.json +100 -0
- package/src/embedded/account-contract-providers/bundle.ts +40 -0
- package/src/embedded/account-contract-providers/lazy.ts +42 -0
- package/src/embedded/account-contract-providers/types.ts +19 -0
- package/src/embedded/embedded_wallet.ts +227 -0
- package/src/embedded/entrypoints/browser.ts +72 -0
- package/src/embedded/entrypoints/node.ts +79 -0
- package/src/embedded/wallet_db.ts +134 -0
- package/src/testing.ts +42 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
3
|
+
export const AccountTypes = [
|
|
4
|
+
'schnorr',
|
|
5
|
+
'ecdsasecp256r1',
|
|
6
|
+
'ecdsasecp256k1'
|
|
7
|
+
];
|
|
8
|
+
function accountKey(field, address) {
|
|
9
|
+
return `${field}:${address.toString()}`;
|
|
10
|
+
}
|
|
11
|
+
export class WalletDB {
|
|
12
|
+
accounts;
|
|
13
|
+
aliases;
|
|
14
|
+
userLog;
|
|
15
|
+
constructor(accounts, aliases, userLog){
|
|
16
|
+
this.accounts = accounts;
|
|
17
|
+
this.aliases = aliases;
|
|
18
|
+
this.userLog = userLog;
|
|
19
|
+
}
|
|
20
|
+
static init(store, userLog) {
|
|
21
|
+
const accounts = store.openMap('accounts');
|
|
22
|
+
const aliases = store.openMap('aliases');
|
|
23
|
+
return new WalletDB(accounts, aliases, userLog);
|
|
24
|
+
}
|
|
25
|
+
async storeAccount(address, { type, secretKey, salt, alias, signingKey }, log = this.userLog) {
|
|
26
|
+
if (alias) {
|
|
27
|
+
await this.aliases.set(`accounts:${alias}`, Buffer.from(address.toString()));
|
|
28
|
+
}
|
|
29
|
+
await this.accounts.set(accountKey('type', address), Buffer.from(type));
|
|
30
|
+
await this.accounts.set(accountKey('sk', address), secretKey.toBuffer());
|
|
31
|
+
await this.accounts.set(accountKey('salt', address), salt.toBuffer());
|
|
32
|
+
await this.accounts.set(accountKey('signingKey', address), 'toBuffer' in signingKey ? signingKey.toBuffer() : signingKey);
|
|
33
|
+
log(`Account stored in database${alias ? ` with alias ${alias}` : ''}`);
|
|
34
|
+
}
|
|
35
|
+
async storeSender(address, alias, log = this.userLog) {
|
|
36
|
+
await this.aliases.set(`senders:${alias}`, Buffer.from(address.toString()));
|
|
37
|
+
log(`Sender stored in database with alias ${alias}`);
|
|
38
|
+
}
|
|
39
|
+
async retrieveAccount(address) {
|
|
40
|
+
const secretKeyBuffer = await this.accounts.getAsync(accountKey('sk', address));
|
|
41
|
+
if (!secretKeyBuffer) {
|
|
42
|
+
throw new Error(`Account "${address.toString()}" does not exist on this wallet.`);
|
|
43
|
+
}
|
|
44
|
+
const [saltBuffer, typeBuffer, signingKey] = await Promise.all([
|
|
45
|
+
this.accounts.getAsync(accountKey('salt', address)),
|
|
46
|
+
this.accounts.getAsync(accountKey('type', address)),
|
|
47
|
+
this.accounts.getAsync(accountKey('signingKey', address))
|
|
48
|
+
]);
|
|
49
|
+
const secretKey = Fr.fromBuffer(secretKeyBuffer);
|
|
50
|
+
const salt = Fr.fromBuffer(saltBuffer);
|
|
51
|
+
const type = typeBuffer.toString('utf8');
|
|
52
|
+
return {
|
|
53
|
+
address,
|
|
54
|
+
secretKey,
|
|
55
|
+
salt,
|
|
56
|
+
type,
|
|
57
|
+
signingKey: signingKey
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async listAccounts() {
|
|
61
|
+
// Read aliases and account addresses in parallel using range queries
|
|
62
|
+
const [aliasesByAddress, accountAddresses] = await Promise.all([
|
|
63
|
+
this.#readAccountAliases(),
|
|
64
|
+
this.#readAccountAddresses()
|
|
65
|
+
]);
|
|
66
|
+
return accountAddresses.map((addressStr)=>({
|
|
67
|
+
alias: aliasesByAddress.get(addressStr) ?? '',
|
|
68
|
+
item: AztecAddress.fromString(addressStr)
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
async listSenders() {
|
|
72
|
+
const result = [];
|
|
73
|
+
for await (const [alias, item] of this.aliases.entriesAsync({
|
|
74
|
+
start: 'senders:',
|
|
75
|
+
end: 'senders:\uffff'
|
|
76
|
+
})){
|
|
77
|
+
result.push({
|
|
78
|
+
alias: alias.slice('senders:'.length),
|
|
79
|
+
item: AztecAddress.fromString(item.toString())
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
async #readAccountAliases() {
|
|
85
|
+
const aliasesByAddress = new Map();
|
|
86
|
+
for await (const [alias, item] of this.aliases.entriesAsync({
|
|
87
|
+
start: 'accounts:',
|
|
88
|
+
end: 'accounts:\uffff'
|
|
89
|
+
})){
|
|
90
|
+
const address = item.toString();
|
|
91
|
+
aliasesByAddress.set(address, alias.slice('accounts:'.length));
|
|
92
|
+
}
|
|
93
|
+
return aliasesByAddress;
|
|
94
|
+
}
|
|
95
|
+
async #readAccountAddresses() {
|
|
96
|
+
const addresses = [];
|
|
97
|
+
// Range query on 'type:' prefix — one entry per account, avoids scanning sk/salt/signingKey entries
|
|
98
|
+
for await (const [key] of this.accounts.entriesAsync({
|
|
99
|
+
start: 'type:',
|
|
100
|
+
end: 'type:\uffff'
|
|
101
|
+
})){
|
|
102
|
+
addresses.push(key.slice('type:'.length));
|
|
103
|
+
}
|
|
104
|
+
return addresses;
|
|
105
|
+
}
|
|
106
|
+
async deleteAccount(address) {
|
|
107
|
+
await Promise.all([
|
|
108
|
+
this.accounts.delete(accountKey('sk', address)),
|
|
109
|
+
this.accounts.delete(accountKey('salt', address)),
|
|
110
|
+
this.accounts.delete(accountKey('type', address)),
|
|
111
|
+
this.accounts.delete(accountKey('signingKey', address))
|
|
112
|
+
]);
|
|
113
|
+
// Clean up alias if one exists
|
|
114
|
+
const aliasesByAddress = await this.#readAccountAliases();
|
|
115
|
+
const alias = aliasesByAddress.get(address.toString());
|
|
116
|
+
if (alias) {
|
|
117
|
+
await this.aliases.delete(`accounts:${alias}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InitialAccountData } from '@aztec/accounts/testing';
|
|
2
|
+
import type { WaitOpts } from '@aztec/aztec.js/contracts';
|
|
3
|
+
import type { AccountManager } from '@aztec/aztec.js/wallet';
|
|
4
|
+
import type { Fq, Fr } from '@aztec/foundation/curves/bn254';
|
|
5
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
+
interface WalletWithSchnorrAccounts {
|
|
7
|
+
createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq, alias?: string): Promise<AccountManager>;
|
|
8
|
+
}
|
|
9
|
+
export declare function deployFundedSchnorrAccounts(wallet: WalletWithSchnorrAccounts, accountsData: InitialAccountData[], waitOptions?: WaitOpts): Promise<AccountManager[]>;
|
|
10
|
+
export declare function registerInitialLocalNetworkAccountsInWallet(wallet: WalletWithSchnorrAccounts): Promise<AztecAddress[]>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdGluZy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3Rlc3RpbmcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUVsRSxPQUFPLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUMxRCxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM3RCxPQUFPLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRTNELFVBQVUseUJBQXlCO0lBQ2pDLG9CQUFvQixDQUFDLE1BQU0sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxDQUFDLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQztDQUN0RztBQUVELHdCQUFzQiwyQkFBMkIsQ0FDL0MsTUFBTSxFQUFFLHlCQUF5QixFQUNqQyxZQUFZLEVBQUUsa0JBQWtCLEVBQUUsRUFDbEMsV0FBVyxDQUFDLEVBQUUsUUFBUSw2QkFnQnZCO0FBRUQsd0JBQXNCLDJDQUEyQyxDQUMvRCxNQUFNLEVBQUUseUJBQXlCLEdBQ2hDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQU96QiJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,UAAU,yBAAyB;IACjC,oBAAoB,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACtG;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,yBAAyB,EACjC,YAAY,EAAE,kBAAkB,EAAE,EAClC,WAAW,CAAC,EAAE,QAAQ,6BAgBvB;AAED,wBAAsB,2CAA2C,CAC/D,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,YAAY,EAAE,CAAC,CAOzB"}
|
package/dest/testing.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getInitialTestAccountsData } from '@aztec/accounts/testing/lazy';
|
|
2
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
3
|
+
export async function deployFundedSchnorrAccounts(wallet, accountsData, waitOptions) {
|
|
4
|
+
const accountManagers = [];
|
|
5
|
+
// Serial due to https://github.com/AztecProtocol/aztec-packages/issues/12045
|
|
6
|
+
for(let i = 0; i < accountsData.length; i++){
|
|
7
|
+
const { secret, salt, signingKey } = accountsData[i];
|
|
8
|
+
const accountManager = await wallet.createSchnorrAccount(secret, salt, signingKey);
|
|
9
|
+
const deployMethod = await accountManager.getDeployMethod();
|
|
10
|
+
await deployMethod.send({
|
|
11
|
+
from: AztecAddress.ZERO,
|
|
12
|
+
skipClassPublication: i !== 0,
|
|
13
|
+
wait: waitOptions
|
|
14
|
+
});
|
|
15
|
+
accountManagers.push(accountManager);
|
|
16
|
+
}
|
|
17
|
+
return accountManagers;
|
|
18
|
+
}
|
|
19
|
+
export async function registerInitialLocalNetworkAccountsInWallet(wallet) {
|
|
20
|
+
const testAccounts = await getInitialTestAccountsData();
|
|
21
|
+
return Promise.all(testAccounts.map(async (account)=>{
|
|
22
|
+
return (await wallet.createSchnorrAccount(account.secret, account.salt, account.signingKey)).address;
|
|
23
|
+
}));
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aztec/wallets",
|
|
3
|
+
"homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/wallets",
|
|
4
|
+
"version": "0.0.1-commit.033589e",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./embedded": {
|
|
8
|
+
"browser": {
|
|
9
|
+
"types": "./dest/embedded/entrypoints/browser.d.ts",
|
|
10
|
+
"default": "./dest/embedded/entrypoints/browser.js"
|
|
11
|
+
},
|
|
12
|
+
"default": {
|
|
13
|
+
"types": "./dest/embedded/entrypoints/node.d.ts",
|
|
14
|
+
"default": "./dest/embedded/entrypoints/node.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"./testing": "./dest/testing.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "yarn clean && ../scripts/tsc.sh",
|
|
21
|
+
"build:dev": "../scripts/tsc.sh --watch",
|
|
22
|
+
"build:ts": "../scripts/tsc.sh",
|
|
23
|
+
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
24
|
+
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
|
|
25
|
+
},
|
|
26
|
+
"inherits": [
|
|
27
|
+
"../package.common.json"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@aztec/accounts": "0.0.1-commit.033589e",
|
|
31
|
+
"@aztec/aztec.js": "0.0.1-commit.033589e",
|
|
32
|
+
"@aztec/entrypoints": "0.0.1-commit.033589e",
|
|
33
|
+
"@aztec/foundation": "0.0.1-commit.033589e",
|
|
34
|
+
"@aztec/kv-store": "0.0.1-commit.033589e",
|
|
35
|
+
"@aztec/protocol-contracts": "0.0.1-commit.033589e",
|
|
36
|
+
"@aztec/pxe": "0.0.1-commit.033589e",
|
|
37
|
+
"@aztec/stdlib": "0.0.1-commit.033589e",
|
|
38
|
+
"@aztec/wallet-sdk": "0.0.1-commit.033589e"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@jest/globals": "^30.0.0",
|
|
42
|
+
"@typescript/native-preview": "7.0.0-dev.20260113.1",
|
|
43
|
+
"jest": "^30.0.0",
|
|
44
|
+
"ts-node": "^10.9.1",
|
|
45
|
+
"typescript": "^5.3.3"
|
|
46
|
+
},
|
|
47
|
+
"typedocOptions": {
|
|
48
|
+
"entryPoints": [
|
|
49
|
+
"./src/embedded/entrypoints/node.ts",
|
|
50
|
+
"./src/testing.ts"
|
|
51
|
+
],
|
|
52
|
+
"name": "Wallets",
|
|
53
|
+
"tsconfig": "./tsconfig.json"
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"dest",
|
|
57
|
+
"src",
|
|
58
|
+
"!*.test.*"
|
|
59
|
+
],
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20.10"
|
|
62
|
+
},
|
|
63
|
+
"jest": {
|
|
64
|
+
"extensionsToTreatAsEsm": [
|
|
65
|
+
".ts"
|
|
66
|
+
],
|
|
67
|
+
"transform": {
|
|
68
|
+
"^.+\\.tsx?$": [
|
|
69
|
+
"@swc/jest",
|
|
70
|
+
{
|
|
71
|
+
"jsc": {
|
|
72
|
+
"parser": {
|
|
73
|
+
"syntax": "typescript",
|
|
74
|
+
"decorators": true
|
|
75
|
+
},
|
|
76
|
+
"transform": {
|
|
77
|
+
"decoratorVersion": "2022-03"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"moduleNameMapper": {
|
|
84
|
+
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
|
|
85
|
+
},
|
|
86
|
+
"reporters": [
|
|
87
|
+
"default"
|
|
88
|
+
],
|
|
89
|
+
"testTimeout": 120000,
|
|
90
|
+
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
|
|
91
|
+
"rootDir": "./src",
|
|
92
|
+
"setupFiles": [
|
|
93
|
+
"../../foundation/src/jest/setup.mjs"
|
|
94
|
+
],
|
|
95
|
+
"setupFilesAfterEnv": [
|
|
96
|
+
"../../foundation/src/jest/setupAfterEnv.mjs"
|
|
97
|
+
],
|
|
98
|
+
"testEnvironment": "../../foundation/src/jest/env.mjs"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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 type { Account, AccountContract } from '@aztec/aztec.js/account';
|
|
5
|
+
import type { Fq } from '@aztec/foundation/curves/bn254';
|
|
6
|
+
import { getCanonicalMultiCallEntrypoint } from '@aztec/protocol-contracts/multi-call-entrypoint';
|
|
7
|
+
import type { ContractArtifact } from '@aztec/stdlib/abi';
|
|
8
|
+
import type { CompleteAddress, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
9
|
+
|
|
10
|
+
import type { AccountContractsProvider } from './types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Loads account contract artifacts eagerly via static imports.
|
|
14
|
+
* Designed for Node.js environments where all artifacts are available at startup.
|
|
15
|
+
*/
|
|
16
|
+
export class BundleAccountContractsProvider implements AccountContractsProvider {
|
|
17
|
+
getSchnorrAccountContract(signingKey: Fq): Promise<AccountContract> {
|
|
18
|
+
return Promise.resolve(new SchnorrAccountContract(signingKey));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getEcdsaRAccountContract(signingKey: Buffer): Promise<AccountContract> {
|
|
22
|
+
return Promise.resolve(new EcdsaRAccountContract(signingKey));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getEcdsaKAccountContract(signingKey: Buffer): Promise<AccountContract> {
|
|
26
|
+
return Promise.resolve(new EcdsaKAccountContract(signingKey));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getStubAccountContractArtifact(): Promise<ContractArtifact> {
|
|
30
|
+
return Promise.resolve(StubAccountContractArtifact);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
createStubAccount(address: CompleteAddress): Promise<Account> {
|
|
34
|
+
return Promise.resolve(createStubAccount(address));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getMulticallContract(): Promise<{ instance: ContractInstanceWithAddress; artifact: ContractArtifact }> {
|
|
38
|
+
return getCanonicalMultiCallEntrypoint();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Account, AccountContract } from '@aztec/aztec.js/account';
|
|
2
|
+
import type { Fq } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { getCanonicalMultiCallEntrypoint } from '@aztec/protocol-contracts/multi-call-entrypoint/lazy';
|
|
4
|
+
import type { ContractArtifact } from '@aztec/stdlib/abi';
|
|
5
|
+
import type { CompleteAddress, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
6
|
+
|
|
7
|
+
import type { AccountContractsProvider } from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Loads account contract artifacts lazily via dynamic imports.
|
|
11
|
+
* Designed for browser environments where code splitting reduces initial bundle size.
|
|
12
|
+
*/
|
|
13
|
+
export class LazyAccountContractsProvider implements AccountContractsProvider {
|
|
14
|
+
async getSchnorrAccountContract(signingKey: Fq): Promise<AccountContract> {
|
|
15
|
+
const { SchnorrAccountContract } = await import('@aztec/accounts/schnorr/lazy');
|
|
16
|
+
return new SchnorrAccountContract(signingKey);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getEcdsaRAccountContract(signingKey: Buffer): Promise<AccountContract> {
|
|
20
|
+
const { EcdsaRAccountContract } = await import('@aztec/accounts/ecdsa/lazy');
|
|
21
|
+
return new EcdsaRAccountContract(signingKey);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async getEcdsaKAccountContract(signingKey: Buffer): Promise<AccountContract> {
|
|
25
|
+
const { EcdsaKAccountContract } = await import('@aztec/accounts/ecdsa/lazy');
|
|
26
|
+
return new EcdsaKAccountContract(signingKey);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getStubAccountContractArtifact(): Promise<ContractArtifact> {
|
|
30
|
+
const { getStubAccountContractArtifact } = await import('@aztec/accounts/stub/lazy');
|
|
31
|
+
return getStubAccountContractArtifact();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async createStubAccount(address: CompleteAddress): Promise<Account> {
|
|
35
|
+
const { createStubAccount } = await import('@aztec/accounts/stub/lazy');
|
|
36
|
+
return createStubAccount(address);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getMulticallContract(): Promise<{ instance: ContractInstanceWithAddress; artifact: ContractArtifact }> {
|
|
40
|
+
return getCanonicalMultiCallEntrypoint();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Account, AccountContract } from '@aztec/aztec.js/account';
|
|
2
|
+
import type { Fq } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import type { ContractArtifact } from '@aztec/stdlib/abi';
|
|
4
|
+
import type { CompleteAddress, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Provides account contract implementations and stub accounts for the EmbeddedWallet.
|
|
8
|
+
* Two implementations exist:
|
|
9
|
+
* - LazyAccountContractsProvider: uses dynamic imports for browser environments
|
|
10
|
+
* - EagerAccountContractsProvider: uses static imports for Node.js environments
|
|
11
|
+
*/
|
|
12
|
+
export interface AccountContractsProvider {
|
|
13
|
+
getSchnorrAccountContract(signingKey: Fq): Promise<AccountContract>;
|
|
14
|
+
getEcdsaRAccountContract(signingKey: Buffer): Promise<AccountContract>;
|
|
15
|
+
getEcdsaKAccountContract(signingKey: Buffer): Promise<AccountContract>;
|
|
16
|
+
getStubAccountContractArtifact(): Promise<ContractArtifact>;
|
|
17
|
+
getMulticallContract(): Promise<{ instance: ContractInstanceWithAddress; artifact: ContractArtifact }>;
|
|
18
|
+
createStubAccount(address: CompleteAddress): Promise<Account>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { type Account, SignerlessAccount } from '@aztec/aztec.js/account';
|
|
2
|
+
import type { Aliased } from '@aztec/aztec.js/wallet';
|
|
3
|
+
import { AccountManager } from '@aztec/aztec.js/wallet';
|
|
4
|
+
import type { DefaultAccountEntrypointOptions } from '@aztec/entrypoints/account';
|
|
5
|
+
import { Fq, Fr } from '@aztec/foundation/curves/bn254';
|
|
6
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
7
|
+
import type { AccessScopes, PXEConfig, PXECreationOptions } from '@aztec/pxe/client/lazy';
|
|
8
|
+
import type { PXE } from '@aztec/pxe/server';
|
|
9
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
10
|
+
import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
|
|
11
|
+
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
|
|
12
|
+
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
13
|
+
import { ExecutionPayload, type TxSimulationResult, mergeExecutionPayloads } from '@aztec/stdlib/tx';
|
|
14
|
+
import { BaseWallet, type FeeOptions } from '@aztec/wallet-sdk/base-wallet';
|
|
15
|
+
|
|
16
|
+
import type { AccountContractsProvider } from './account-contract-providers/types.js';
|
|
17
|
+
import { type AccountType, WalletDB } from './wallet_db.js';
|
|
18
|
+
|
|
19
|
+
export type EmbeddedWalletOptions = {
|
|
20
|
+
/** Parent logger. Child loggers are derived via createChild() for each subsystem. */
|
|
21
|
+
logger?: Logger;
|
|
22
|
+
/** Use ephemeral (in-memory) stores. Data will not persist across sessions. */
|
|
23
|
+
ephemeral?: boolean;
|
|
24
|
+
/** Override PXE configuration. */
|
|
25
|
+
pxeConfig?: Partial<PXEConfig>;
|
|
26
|
+
/** Advanced PXE creation options (custom store, prover, simulator). */
|
|
27
|
+
pxeOptions?: PXECreationOptions;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class EmbeddedWallet extends BaseWallet {
|
|
31
|
+
constructor(
|
|
32
|
+
pxe: PXE,
|
|
33
|
+
aztecNode: AztecNode,
|
|
34
|
+
protected walletDB: WalletDB,
|
|
35
|
+
protected accountContracts: AccountContractsProvider,
|
|
36
|
+
log?: Logger,
|
|
37
|
+
) {
|
|
38
|
+
super(pxe, aztecNode, log);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected async getAccountFromAddress(address: AztecAddress): Promise<Account> {
|
|
42
|
+
if (address.equals(AztecAddress.ZERO)) {
|
|
43
|
+
return new SignerlessAccount();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { secretKey, salt, signingKey, type } = await this.walletDB.retrieveAccount(address);
|
|
47
|
+
const accountManager = await this.createAccountInternal(type, secretKey, salt, signingKey);
|
|
48
|
+
const account = await accountManager.getAccount();
|
|
49
|
+
|
|
50
|
+
if (!account) {
|
|
51
|
+
throw new Error(`Account not found in wallet for address: ${address}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return account;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getAccounts(): Promise<Aliased<AztecAddress>[]> {
|
|
58
|
+
return this.walletDB.listAccounts();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override async registerSender(address: AztecAddress, alias: string) {
|
|
62
|
+
await this.walletDB.storeSender(address, alias);
|
|
63
|
+
return this.pxe.registerSender(address);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override async getAddressBook(): Promise<Aliased<AztecAddress>[]> {
|
|
67
|
+
const senders = await this.pxe.getSenders();
|
|
68
|
+
const storedSenders = await this.walletDB.listSenders();
|
|
69
|
+
for (const storedSender of storedSenders) {
|
|
70
|
+
if (senders.findIndex(sender => sender.equals(storedSender.item)) === -1) {
|
|
71
|
+
await this.pxe.registerSender(storedSender.item);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return storedSenders;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Simulates calls via a stub account entrypoint, bypassing real account authorization.
|
|
79
|
+
* This allows kernelless simulation with contract overrides, skipping expensive
|
|
80
|
+
* private kernel circuit execution.
|
|
81
|
+
*/
|
|
82
|
+
protected override async simulateViaEntrypoint(
|
|
83
|
+
executionPayload: ExecutionPayload,
|
|
84
|
+
from: AztecAddress,
|
|
85
|
+
feeOptions: FeeOptions,
|
|
86
|
+
scopes: AccessScopes,
|
|
87
|
+
_skipTxValidation?: boolean,
|
|
88
|
+
_skipFeeEnforcement?: boolean,
|
|
89
|
+
): Promise<TxSimulationResult> {
|
|
90
|
+
const { account: fromAccount, instance, artifact } = await this.getFakeAccountDataFor(from);
|
|
91
|
+
|
|
92
|
+
const feeExecutionPayload = await feeOptions.walletFeePaymentMethod?.getExecutionPayload();
|
|
93
|
+
const executionOptions: DefaultAccountEntrypointOptions = {
|
|
94
|
+
txNonce: Fr.random(),
|
|
95
|
+
cancellable: this.cancellableTransactions,
|
|
96
|
+
feePaymentMethodOptions: feeOptions.accountFeePaymentMethodOptions,
|
|
97
|
+
};
|
|
98
|
+
const finalExecutionPayload = feeExecutionPayload
|
|
99
|
+
? mergeExecutionPayloads([feeExecutionPayload, executionPayload])
|
|
100
|
+
: executionPayload;
|
|
101
|
+
const chainInfo = await this.getChainInfo();
|
|
102
|
+
const txRequest = await fromAccount.createTxExecutionRequest(
|
|
103
|
+
finalExecutionPayload,
|
|
104
|
+
feeOptions.gasSettings,
|
|
105
|
+
chainInfo,
|
|
106
|
+
executionOptions,
|
|
107
|
+
);
|
|
108
|
+
return this.pxe.simulateTx(txRequest, {
|
|
109
|
+
simulatePublic: true,
|
|
110
|
+
skipFeeEnforcement: true,
|
|
111
|
+
skipTxValidation: true,
|
|
112
|
+
overrides: {
|
|
113
|
+
contracts: { [from.toString()]: { instance, artifact } },
|
|
114
|
+
},
|
|
115
|
+
scopes,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private async getFakeAccountDataFor(address: AztecAddress) {
|
|
120
|
+
// While we have the convention of "Zero address means no auth", and also
|
|
121
|
+
// we don't have a way to trigger kernelless simulations without overrides,
|
|
122
|
+
// we need to explicitly handle the zero address case here by
|
|
123
|
+
// returning the actual multicall contract instead of trying to create a stub account for it.
|
|
124
|
+
if (!address.equals(AztecAddress.ZERO)) {
|
|
125
|
+
const originalAccount = await this.getAccountFromAddress(address);
|
|
126
|
+
if (originalAccount instanceof SignerlessAccount) {
|
|
127
|
+
throw new Error(`Cannot create fake account data for SignerlessAccount at address: ${address}`);
|
|
128
|
+
}
|
|
129
|
+
const originalAddress = (originalAccount as Account).getCompleteAddress();
|
|
130
|
+
const contractInstance = await this.pxe.getContractInstance(originalAddress.address);
|
|
131
|
+
if (!contractInstance) {
|
|
132
|
+
throw new Error(`No contract instance found for address: ${originalAddress.address}`);
|
|
133
|
+
}
|
|
134
|
+
const stubAccount = await this.accountContracts.createStubAccount(originalAddress);
|
|
135
|
+
const stubArtifact = await this.accountContracts.getStubAccountContractArtifact();
|
|
136
|
+
const instance = await getContractInstanceFromInstantiationParams(stubArtifact, {
|
|
137
|
+
salt: Fr.random(),
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
account: stubAccount,
|
|
141
|
+
instance,
|
|
142
|
+
artifact: stubArtifact,
|
|
143
|
+
};
|
|
144
|
+
} else {
|
|
145
|
+
const { instance, artifact } = await this.accountContracts.getMulticallContract();
|
|
146
|
+
const account = new SignerlessAccount();
|
|
147
|
+
return {
|
|
148
|
+
instance,
|
|
149
|
+
account,
|
|
150
|
+
artifact,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
protected async createAccountInternal(
|
|
156
|
+
type: AccountType,
|
|
157
|
+
secret: Fr,
|
|
158
|
+
salt: Fr,
|
|
159
|
+
signingKey: Buffer,
|
|
160
|
+
): Promise<AccountManager> {
|
|
161
|
+
let contract;
|
|
162
|
+
switch (type) {
|
|
163
|
+
case 'schnorr': {
|
|
164
|
+
contract = await this.accountContracts.getSchnorrAccountContract(Fq.fromBuffer(signingKey));
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case 'ecdsasecp256k1': {
|
|
168
|
+
contract = await this.accountContracts.getEcdsaKAccountContract(signingKey);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 'ecdsasecp256r1': {
|
|
172
|
+
contract = await this.accountContracts.getEcdsaRAccountContract(signingKey);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
default: {
|
|
176
|
+
throw new Error(`Unknown account type ${type}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const accountManager = await AccountManager.create(this, secret, contract, salt);
|
|
181
|
+
|
|
182
|
+
const instance = accountManager.getInstance();
|
|
183
|
+
const existingInstance = await this.pxe.getContractInstance(instance.address);
|
|
184
|
+
if (!existingInstance) {
|
|
185
|
+
const existingArtifact = await this.pxe.getContractArtifact(instance.currentContractClassId);
|
|
186
|
+
await this.registerContract(
|
|
187
|
+
instance,
|
|
188
|
+
!existingArtifact ? await accountManager.getAccountContract().getContractArtifact() : undefined,
|
|
189
|
+
accountManager.getSecretKey(),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return accountManager;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async createAndStoreAccount(
|
|
196
|
+
alias: string,
|
|
197
|
+
type: AccountType,
|
|
198
|
+
secret: Fr,
|
|
199
|
+
salt: Fr,
|
|
200
|
+
signingKey: Buffer,
|
|
201
|
+
): Promise<AccountManager> {
|
|
202
|
+
const accountManager = await this.createAccountInternal(type, secret, salt, signingKey);
|
|
203
|
+
await this.walletDB.storeAccount(accountManager.address, { type, secretKey: secret, salt, alias, signingKey });
|
|
204
|
+
return accountManager;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq, alias?: string): Promise<AccountManager> {
|
|
208
|
+
const sk = signingKey ?? deriveSigningKey(secret);
|
|
209
|
+
return this.createAndStoreAccount(alias ?? '', 'schnorr', secret, salt, sk.toBuffer());
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer, alias?: string): Promise<AccountManager> {
|
|
213
|
+
return this.createAndStoreAccount(alias ?? '', 'ecdsasecp256r1', secret, salt, signingKey);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer, alias?: string): Promise<AccountManager> {
|
|
217
|
+
return this.createAndStoreAccount(alias ?? '', 'ecdsasecp256k1', secret, salt, signingKey);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
setMinFeePadding(value?: number) {
|
|
221
|
+
this.minFeePadding = value ?? 0.5;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
stop() {
|
|
225
|
+
return this.pxe.stop();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type AztecNode, createAztecNodeClient } from '@aztec/aztec.js/node';
|
|
2
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { createStore, openTmpStore } from '@aztec/kv-store/indexeddb';
|
|
4
|
+
import { type PXE, type PXECreationOptions, createPXE } from '@aztec/pxe/client/lazy';
|
|
5
|
+
import { type PXEConfig, getPXEConfig } from '@aztec/pxe/config';
|
|
6
|
+
|
|
7
|
+
import { LazyAccountContractsProvider } from '../account-contract-providers/lazy.js';
|
|
8
|
+
import type { AccountContractsProvider } from '../account-contract-providers/types.js';
|
|
9
|
+
import { EmbeddedWallet, type EmbeddedWalletOptions } from '../embedded_wallet.js';
|
|
10
|
+
import { WalletDB } from '../wallet_db.js';
|
|
11
|
+
|
|
12
|
+
export class BrowserEmbeddedWallet extends EmbeddedWallet {
|
|
13
|
+
static async create<T extends BrowserEmbeddedWallet = BrowserEmbeddedWallet>(
|
|
14
|
+
this: new (
|
|
15
|
+
pxe: PXE,
|
|
16
|
+
aztecNode: AztecNode,
|
|
17
|
+
walletDB: WalletDB,
|
|
18
|
+
accountContracts: AccountContractsProvider,
|
|
19
|
+
log?: Logger,
|
|
20
|
+
) => T,
|
|
21
|
+
nodeOrUrl: string | AztecNode,
|
|
22
|
+
options: EmbeddedWalletOptions = {},
|
|
23
|
+
): Promise<T> {
|
|
24
|
+
const rootLogger = options.logger ?? createLogger('embedded-wallet');
|
|
25
|
+
|
|
26
|
+
const aztecNode = typeof nodeOrUrl === 'string' ? createAztecNodeClient(nodeOrUrl) : nodeOrUrl;
|
|
27
|
+
const l1Contracts = await aztecNode.getL1ContractAddresses();
|
|
28
|
+
|
|
29
|
+
const pxeConfig: PXEConfig = Object.assign(getPXEConfig(), {
|
|
30
|
+
proverEnabled: options.pxeConfig?.proverEnabled ?? false,
|
|
31
|
+
dataDirectory: `pxe_data_${l1Contracts.rollupAddress}`,
|
|
32
|
+
...options.pxeConfig,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (options.ephemeral) {
|
|
36
|
+
delete pxeConfig.dataDirectory;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const pxeOptions: PXECreationOptions = {
|
|
40
|
+
...options.pxeOptions,
|
|
41
|
+
loggers: {
|
|
42
|
+
store: rootLogger.createChild('pxe:data'),
|
|
43
|
+
pxe: rootLogger.createChild('pxe:service'),
|
|
44
|
+
prover: rootLogger.createChild('pxe:prover'),
|
|
45
|
+
...options.pxeOptions?.loggers,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const pxe = await createPXE(aztecNode, pxeConfig, pxeOptions);
|
|
50
|
+
|
|
51
|
+
const walletDBStore = options.ephemeral
|
|
52
|
+
? await openTmpStore(true)
|
|
53
|
+
: await createStore(
|
|
54
|
+
'wallet_data',
|
|
55
|
+
{
|
|
56
|
+
dataDirectory: `wallet_data_${l1Contracts.rollupAddress}`,
|
|
57
|
+
dataStoreMapSizeKb: pxeConfig.dataStoreMapSizeKb,
|
|
58
|
+
l1Contracts,
|
|
59
|
+
},
|
|
60
|
+
1,
|
|
61
|
+
rootLogger.createChild('wallet:data'),
|
|
62
|
+
);
|
|
63
|
+
const walletDB = WalletDB.init(walletDBStore, rootLogger.createChild('wallet:db').info);
|
|
64
|
+
|
|
65
|
+
return new this(pxe, aztecNode, walletDB, new LazyAccountContractsProvider(), rootLogger) as T;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { BrowserEmbeddedWallet as EmbeddedWallet };
|
|
70
|
+
export type { EmbeddedWalletOptions } from '../embedded_wallet.js';
|
|
71
|
+
export { WalletDB } from '../wallet_db.js';
|
|
72
|
+
export type { AccountType } from '../wallet_db.js';
|