@aztec/cli-wallet 0.0.1-commit.b9865e97 → 0.0.1-commit.be03c316
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/cmds/check_tx.js +8 -6
- package/dest/cmds/create_account.d.ts +4 -2
- package/dest/cmds/create_account.d.ts.map +1 -1
- package/dest/cmds/create_account.js +26 -10
- package/dest/cmds/deploy.js +3 -6
- package/dest/cmds/deploy_account.js +4 -7
- package/dest/cmds/import_test_accounts.d.ts +1 -1
- package/dest/cmds/import_test_accounts.d.ts.map +1 -1
- package/dest/cmds/import_test_accounts.js +2 -1
- package/dest/cmds/index.d.ts +1 -1
- package/dest/cmds/index.d.ts.map +1 -1
- package/dest/cmds/index.js +8 -7
- package/dest/cmds/send.js +3 -6
- package/dest/cmds/simulate.d.ts +1 -1
- package/dest/cmds/simulate.d.ts.map +1 -1
- package/dest/cmds/simulate.js +2 -1
- package/dest/storage/wallet_db.d.ts +7 -4
- package/dest/storage/wallet_db.d.ts.map +1 -1
- package/dest/storage/wallet_db.js +15 -1
- package/dest/utils/constants.d.ts +2 -2
- package/dest/utils/constants.d.ts.map +1 -1
- package/dest/utils/constants.js +1 -0
- package/dest/utils/options/fees.d.ts +1 -1
- package/dest/utils/options/fees.d.ts.map +1 -1
- package/dest/utils/options/fees.js +4 -1
- package/dest/utils/options/options.d.ts +2 -2
- package/dest/utils/options/options.d.ts.map +1 -1
- package/dest/utils/options/options.js +5 -5
- package/dest/utils/wallet.d.ts +25 -5
- package/dest/utils/wallet.d.ts.map +1 -1
- package/dest/utils/wallet.js +89 -67
- package/package.json +15 -15
- package/src/cmds/check_tx.ts +11 -10
- package/src/cmds/create_account.ts +27 -15
- package/src/cmds/deploy.ts +3 -3
- package/src/cmds/deploy_account.ts +4 -4
- package/src/cmds/import_test_accounts.ts +12 -1
- package/src/cmds/index.ts +11 -9
- package/src/cmds/send.ts +3 -3
- package/src/cmds/simulate.ts +2 -1
- package/src/storage/wallet_db.ts +24 -2
- package/src/utils/constants.ts +7 -1
- package/src/utils/options/fees.ts +4 -2
- package/src/utils/options/options.ts +5 -5
- package/src/utils/wallet.ts +132 -85
package/src/cmds/send.ts
CHANGED
|
@@ -42,10 +42,10 @@ export async function send(
|
|
|
42
42
|
const localStart = performance.now();
|
|
43
43
|
const sim = await call.simulate({
|
|
44
44
|
...sendOptions,
|
|
45
|
-
|
|
45
|
+
includeMetadata: true,
|
|
46
46
|
});
|
|
47
|
-
//
|
|
48
|
-
const estimatedGas = sim.
|
|
47
|
+
// includeMetadata: true guarantees these fields are present
|
|
48
|
+
const estimatedGas = await wallet.estimateGasLimits(sim.gasUsed!);
|
|
49
49
|
const stats = sim.stats!;
|
|
50
50
|
|
|
51
51
|
if (feeOpts.estimateOnly) {
|
package/src/cmds/simulate.ts
CHANGED
|
@@ -44,7 +44,8 @@ export async function simulate(
|
|
|
44
44
|
if (!metadata.instance) {
|
|
45
45
|
return undefined;
|
|
46
46
|
}
|
|
47
|
-
const
|
|
47
|
+
const classId = metadata.updatedContractClassId ?? metadata.instance.originalContractClassId;
|
|
48
|
+
const artifact = await wallet.getContractArtifact(classId);
|
|
48
49
|
return artifact;
|
|
49
50
|
},
|
|
50
51
|
log,
|
package/src/storage/wallet_db.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
|
|
2
3
|
import type { LogFn } from '@aztec/foundation/log';
|
|
3
4
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
4
5
|
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
@@ -88,13 +89,28 @@ export class WalletDB {
|
|
|
88
89
|
address: AztecAddress,
|
|
89
90
|
{
|
|
90
91
|
type,
|
|
92
|
+
signingKey,
|
|
91
93
|
secretKey,
|
|
92
94
|
salt,
|
|
93
95
|
alias,
|
|
94
96
|
publicKey,
|
|
95
|
-
}: {
|
|
97
|
+
}: {
|
|
98
|
+
type: AccountType;
|
|
99
|
+
signingKey?: GrumpkinScalar;
|
|
100
|
+
secretKey?: Fr;
|
|
101
|
+
salt: Fr;
|
|
102
|
+
alias: string | undefined;
|
|
103
|
+
publicKey: string | undefined;
|
|
104
|
+
},
|
|
96
105
|
log: LogFn,
|
|
97
106
|
) {
|
|
107
|
+
// Even though the privacy secret key is sometimes derived from the signing key, we store it in the database
|
|
108
|
+
// regardless as that is not always the case. Local-key accounts also store their signing key, while external-key
|
|
109
|
+
// accounts such as SSH store only the privacy secret.
|
|
110
|
+
if (!secretKey) {
|
|
111
|
+
throw new Error('Cannot store account without a secret key');
|
|
112
|
+
}
|
|
113
|
+
|
|
98
114
|
let publicSigningKey: Buffer | undefined;
|
|
99
115
|
if (type === 'ecdsasecp256r1ssh' && publicKey) {
|
|
100
116
|
publicSigningKey = extractECDSAPublicKeyFromBase64String(publicKey);
|
|
@@ -106,6 +122,9 @@ export class WalletDB {
|
|
|
106
122
|
}
|
|
107
123
|
await this.#accounts.set(`${address.toString()}:type`, Buffer.from(type));
|
|
108
124
|
await this.#accounts.set(`${address.toString()}:sk`, secretKey.toBuffer());
|
|
125
|
+
if (signingKey) {
|
|
126
|
+
await this.#accounts.set(`${address.toString()}:signing`, signingKey.toBuffer());
|
|
127
|
+
}
|
|
109
128
|
await this.#accounts.set(`${address.toString()}:salt`, salt.toBuffer());
|
|
110
129
|
if (publicSigningKey) {
|
|
111
130
|
await this.#accounts.set(`${address.toString()}:publicSigningKey`, publicSigningKey);
|
|
@@ -226,10 +245,13 @@ export class WalletDB {
|
|
|
226
245
|
if (!secretKeyBuffer) {
|
|
227
246
|
throw new Error(`Could not find ${address}:sk. Account "${address.toString}" does not exist on this wallet.`);
|
|
228
247
|
}
|
|
248
|
+
const signingKeyBuffer = await this.#accounts.getAsync(`${address.toString()}:signing`);
|
|
229
249
|
const secretKey = Fr.fromBuffer(secretKeyBuffer);
|
|
250
|
+
// External-key accounts (e.g. SSH) store no signing key.
|
|
251
|
+
const signingKey = signingKeyBuffer ? GrumpkinScalar.fromBuffer(signingKeyBuffer) : undefined;
|
|
230
252
|
const salt = Fr.fromBuffer((await this.#accounts.getAsync(`${address.toString()}:salt`))!);
|
|
231
253
|
const type = (await this.#accounts.getAsync(`${address.toString()}:type`))!.toString('utf8') as AccountType;
|
|
232
|
-
return { address, secretKey, salt, type };
|
|
254
|
+
return { address, signingKey, secretKey, salt, type };
|
|
233
255
|
}
|
|
234
256
|
|
|
235
257
|
async storeAlias(type: AliasType, key: string, value: Buffer, log: LogFn) {
|
package/src/utils/constants.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export const MIN_FEE_PADDING = 0.5;
|
|
2
2
|
|
|
3
|
-
export const AccountTypes = [
|
|
3
|
+
export const AccountTypes = [
|
|
4
|
+
'schnorr',
|
|
5
|
+
'schnorr_initializerless',
|
|
6
|
+
'ecdsasecp256r1',
|
|
7
|
+
'ecdsasecp256r1ssh',
|
|
8
|
+
'ecdsasecp256k1',
|
|
9
|
+
] as const;
|
|
4
10
|
export type AccountType = (typeof AccountTypes)[number];
|
|
@@ -137,7 +137,7 @@ export function parsePaymentMethod(
|
|
|
137
137
|
if (!parsed.asset) {
|
|
138
138
|
throw new Error('Missing "asset" in payment option');
|
|
139
139
|
}
|
|
140
|
-
return AztecAddress.
|
|
140
|
+
return AztecAddress.fromStringUnsafe(parsed.asset);
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
return async (wallet: Wallet, from: AztecAddress, gasSettings: GasSettings) => {
|
|
@@ -259,7 +259,9 @@ export class CLIFeeArgs {
|
|
|
259
259
|
async toUserFeeOptions(node: AztecNode, wallet: Wallet, from: AztecAddress): Promise<ParsedFeeOptions> {
|
|
260
260
|
const minFees = await this.getMinFees(node);
|
|
261
261
|
const maxFeesPerGas = minFees.mul(1 + MIN_FEE_PADDING);
|
|
262
|
-
const
|
|
262
|
+
const { txsLimits } = await node.getNodeInfo();
|
|
263
|
+
const gasLimits = this.gasSettings.gasLimits ?? Gas.from(txsLimits.gas);
|
|
264
|
+
const gasSettings = GasSettings.fallback({ ...this.gasSettings, gasLimits, maxFeesPerGas });
|
|
263
265
|
const paymentMethod = await this.paymentMethod(wallet, from, gasSettings);
|
|
264
266
|
return {
|
|
265
267
|
paymentMethod,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TxHash } from '@aztec/aztec.js/tx';
|
|
2
|
-
import { parseAztecAddress,
|
|
2
|
+
import { parseAztecAddress, parseSigningKey, parseTxHash } from '@aztec/cli/utils';
|
|
3
3
|
import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
4
4
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
5
|
|
|
@@ -64,13 +64,13 @@ export function aliasedAddressParser(defaultPrefix: AliasType, address: string,
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
export function
|
|
67
|
+
export function aliasedSigningKeyParser(sk: string, db?: WalletDB) {
|
|
68
68
|
if (sk.startsWith('0x')) {
|
|
69
|
-
return
|
|
69
|
+
return parseSigningKey(sk);
|
|
70
70
|
} else {
|
|
71
|
-
const prefixed = `${sk.startsWith('accounts') ? '' : 'accounts'}:${sk.endsWith(':
|
|
71
|
+
const prefixed = `${sk.startsWith('accounts') ? '' : 'accounts'}:${sk.endsWith(':signing') ? sk : `${sk}:signing`}`;
|
|
72
72
|
const rawSk = db ? db.tryRetrieveAlias(prefixed) : sk;
|
|
73
|
-
return
|
|
73
|
+
return parseSigningKey(rawSk);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
package/src/utils/wallet.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { EcdsaRAccountContract, EcdsaRSSHAccountContract } from '@aztec/accounts/ecdsa';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from '@aztec/accounts/stub
|
|
1
|
+
import { EcdsaKAccountContract, EcdsaRAccountContract, EcdsaRSSHAccountContract } from '@aztec/accounts/ecdsa';
|
|
2
|
+
import { StubEcdsaAccountContractArtifact, createStubEcdsaAccount } from '@aztec/accounts/ecdsa/stub';
|
|
3
|
+
import { SchnorrAccountContract, SchnorrInitializerlessAccountContract } from '@aztec/accounts/schnorr';
|
|
4
|
+
import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from '@aztec/accounts/schnorr/stub';
|
|
5
5
|
import { getIdentities } from '@aztec/accounts/utils';
|
|
6
6
|
import { type Account, type AccountContract, NO_FROM } from '@aztec/aztec.js/account';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ContractFunctionInteraction,
|
|
9
|
+
type InteractionFeeOptions,
|
|
10
|
+
getContractClassFromArtifact,
|
|
11
|
+
} from '@aztec/aztec.js/contracts';
|
|
8
12
|
import type { AztecNode } from '@aztec/aztec.js/node';
|
|
9
|
-
import { AccountManager, type Aliased
|
|
13
|
+
import { AccountManager, type Aliased } from '@aztec/aztec.js/wallet';
|
|
10
14
|
import { TxSimulationResultWithAppOffset } from '@aztec/aztec.js/wallet';
|
|
11
15
|
import type { DefaultAccountEntrypointOptions } from '@aztec/entrypoints/account';
|
|
12
16
|
import { DefaultEntrypoint } from '@aztec/entrypoints/default';
|
|
13
17
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
18
|
+
import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
|
|
14
19
|
import type { LogFn } from '@aztec/foundation/log';
|
|
15
20
|
import type { NotesFilter } from '@aztec/pxe/client/lazy';
|
|
16
21
|
import type { PXEConfig } from '@aztec/pxe/config';
|
|
@@ -18,16 +23,18 @@ import type { PXE } from '@aztec/pxe/server';
|
|
|
18
23
|
import { createPXE, getPXEConfig } from '@aztec/pxe/server';
|
|
19
24
|
import { getStandardAuthRegistry } from '@aztec/standard-contracts/auth-registry';
|
|
20
25
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
21
|
-
import {
|
|
26
|
+
import type { Gas, GasUsed } from '@aztec/stdlib/gas';
|
|
22
27
|
import { NoteDao } from '@aztec/stdlib/note';
|
|
23
28
|
import type { SimulationOverrides, TxExecutionRequest, TxProvingResult } from '@aztec/stdlib/tx';
|
|
24
29
|
import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx';
|
|
25
|
-
import { BaseWallet, type SimulateViaEntrypointOptions } from '@aztec/wallet-sdk/base-wallet';
|
|
30
|
+
import { BaseWallet, type SimulateViaEntrypointOptions, getGasLimits } from '@aztec/wallet-sdk/base-wallet';
|
|
26
31
|
|
|
27
32
|
import type { WalletDB } from '../storage/wallet_db.js';
|
|
28
33
|
import type { AccountType } from './constants.js';
|
|
29
34
|
import { extractECDSAPublicKeyFromBase64String } from './ecdsa.js';
|
|
30
|
-
|
|
35
|
+
|
|
36
|
+
/** Padding the CLI wallet applies to simulated gas usage when deriving declared gas limits. */
|
|
37
|
+
const DEFAULT_ESTIMATED_GAS_PADDING = 0.1;
|
|
31
38
|
|
|
32
39
|
export class CLIWallet extends BaseWallet {
|
|
33
40
|
private accountCache = new Map<string, Account>();
|
|
@@ -61,7 +68,8 @@ export class CLIWallet extends BaseWallet {
|
|
|
61
68
|
|
|
62
69
|
private async registerAuthRegistry(): Promise<void> {
|
|
63
70
|
const { instance, artifact } = await getStandardAuthRegistry();
|
|
64
|
-
await this.pxe.
|
|
71
|
+
await this.pxe.registerContractClass(artifact);
|
|
72
|
+
await this.pxe.registerContract(instance);
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
/**
|
|
@@ -77,6 +85,7 @@ export class CLIWallet extends BaseWallet {
|
|
|
77
85
|
await this.pxe.registerContractClass(StubEcdsaAccountContractArtifact);
|
|
78
86
|
|
|
79
87
|
this.stubClassIds.set('schnorr', schnorrClassId);
|
|
88
|
+
this.stubClassIds.set('schnorr_initializerless', schnorrClassId);
|
|
80
89
|
this.stubClassIds.set('ecdsasecp256k1', ecdsaClassId);
|
|
81
90
|
this.stubClassIds.set('ecdsasecp256r1', ecdsaClassId);
|
|
82
91
|
this.stubClassIds.set('ecdsasecp256r1ssh', ecdsaClassId);
|
|
@@ -87,11 +96,21 @@ export class CLIWallet extends BaseWallet {
|
|
|
87
96
|
return Promise.resolve(
|
|
88
97
|
accounts.map(({ key, value }) => {
|
|
89
98
|
const alias = key.includes(':') ? key.slice(key.indexOf(':') + 1) : key;
|
|
90
|
-
return { alias, item: AztecAddress.
|
|
99
|
+
return { alias, item: AztecAddress.fromStringUnsafe(value) };
|
|
91
100
|
}),
|
|
92
101
|
);
|
|
93
102
|
}
|
|
94
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Derives suggested total and teardown gas limits from simulated gas usage, padded and clamped to the
|
|
106
|
+
* network's per-tx admission limits.
|
|
107
|
+
* @param gasUsed - The gas consumed during simulation (from a `simulate({ includeMetadata: true })` result).
|
|
108
|
+
*/
|
|
109
|
+
async estimateGasLimits(gasUsed: GasUsed): Promise<{ gasLimits: Gas; teardownGasLimits: Gas }> {
|
|
110
|
+
const maxTxGasLimits = await this.getMaxTxGasLimits();
|
|
111
|
+
return getGasLimits(gasUsed, maxTxGasLimits, DEFAULT_ESTIMATED_GAS_PADDING);
|
|
112
|
+
}
|
|
113
|
+
|
|
95
114
|
private async createCancellationTxExecutionRequest(
|
|
96
115
|
from: AztecAddress,
|
|
97
116
|
txNonce: Fr,
|
|
@@ -126,7 +145,10 @@ export class CLIWallet extends BaseWallet {
|
|
|
126
145
|
increasedFee: InteractionFeeOptions,
|
|
127
146
|
): Promise<TxProvingResult> {
|
|
128
147
|
const cancellationTxRequest = await this.createCancellationTxExecutionRequest(from, txNonce, increasedFee);
|
|
129
|
-
return await this.pxe.proveTx(cancellationTxRequest, {
|
|
148
|
+
return await this.pxe.proveTx(cancellationTxRequest, {
|
|
149
|
+
scopes: this.scopesFrom(from, [], undefined),
|
|
150
|
+
senderForTags: from,
|
|
151
|
+
});
|
|
130
152
|
}
|
|
131
153
|
|
|
132
154
|
override async getAccountFromAddress(address: AztecAddress) {
|
|
@@ -134,7 +156,7 @@ export class CLIWallet extends BaseWallet {
|
|
|
134
156
|
if (this.accountCache.has(address.toString())) {
|
|
135
157
|
return this.accountCache.get(address.toString())!;
|
|
136
158
|
} else {
|
|
137
|
-
const accountManager = await this.
|
|
159
|
+
const accountManager = await this.retrieveAccount(address);
|
|
138
160
|
account = await accountManager.getAccount();
|
|
139
161
|
}
|
|
140
162
|
|
|
@@ -144,74 +166,115 @@ export class CLIWallet extends BaseWallet {
|
|
|
144
166
|
return account;
|
|
145
167
|
}
|
|
146
168
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async createOrRetrieveAccount(
|
|
159
|
-
address?: AztecAddress,
|
|
160
|
-
secretKey?: Fr,
|
|
161
|
-
type: AccountType = 'schnorr',
|
|
162
|
-
salt?: Fr,
|
|
169
|
+
/**
|
|
170
|
+
* Creates an account from freshly supplied keys. SSH accounts sign with a key held in the agent (resolved from
|
|
171
|
+
* `publicKey`), every other type is rooted on `signingKey`, with `secretKey` as its privacy secret.
|
|
172
|
+
*/
|
|
173
|
+
async createAccount(
|
|
174
|
+
type: AccountType,
|
|
175
|
+
signingKey: GrumpkinScalar | undefined,
|
|
176
|
+
secretKey: Fr | undefined,
|
|
177
|
+
salt: Fr = Fr.ZERO,
|
|
163
178
|
publicKey?: string,
|
|
164
179
|
): Promise<AccountManager> {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
salt
|
|
180
|
+
const publicSigningKey =
|
|
181
|
+
type === 'ecdsasecp256r1ssh' ? await this.resolveSshPublicSigningKey(publicKey) : undefined;
|
|
182
|
+
return this.buildAccount(type, salt, signingKey, secretKey, publicSigningKey);
|
|
183
|
+
}
|
|
168
184
|
|
|
169
|
-
|
|
170
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Retrieves a previously stored account by address, loading its keys, type and salt from the wallet database.
|
|
187
|
+
*/
|
|
188
|
+
async retrieveAccount(address: AztecAddress): Promise<AccountManager> {
|
|
189
|
+
if (!this.db) {
|
|
190
|
+
throw new Error('Cannot retrieve an account without a wallet database');
|
|
171
191
|
}
|
|
172
|
-
|
|
173
|
-
if (!
|
|
174
|
-
throw new Error(
|
|
192
|
+
const { type, signingKey, secretKey, salt } = await this.db.retrieveAccount(address);
|
|
193
|
+
if (type !== 'ecdsasecp256r1ssh' && !signingKey) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Account ${address} has no stored signing key: it was created with an older version of aztec-wallet and is ` +
|
|
196
|
+
`incompatible with this one. Create a new account to continue.`,
|
|
197
|
+
);
|
|
175
198
|
}
|
|
199
|
+
const publicSigningKey =
|
|
200
|
+
type === 'ecdsasecp256r1ssh' ? await this.db.retrieveAccountMetadata(address, 'publicSigningKey') : undefined;
|
|
201
|
+
return this.buildAccount(type, salt, signingKey, secretKey, publicSigningKey);
|
|
202
|
+
}
|
|
176
203
|
|
|
204
|
+
private async buildAccount(
|
|
205
|
+
type: AccountType,
|
|
206
|
+
salt: Fr,
|
|
207
|
+
signingKey: GrumpkinScalar | undefined,
|
|
208
|
+
secretKey: Fr | undefined,
|
|
209
|
+
publicSigningKey: Buffer | undefined,
|
|
210
|
+
): Promise<AccountManager> {
|
|
177
211
|
switch (type) {
|
|
178
|
-
case 'schnorr':
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
212
|
+
case 'schnorr':
|
|
213
|
+
case 'schnorr_initializerless':
|
|
214
|
+
case 'ecdsasecp256r1':
|
|
215
|
+
case 'ecdsasecp256k1': {
|
|
216
|
+
if (!signingKey || !secretKey) {
|
|
217
|
+
throw new Error('Cannot build account without signing key and secret key');
|
|
218
|
+
}
|
|
219
|
+
const contract =
|
|
220
|
+
type === 'schnorr'
|
|
221
|
+
? new SchnorrAccountContract(signingKey)
|
|
222
|
+
: type === 'schnorr_initializerless'
|
|
223
|
+
? new SchnorrInitializerlessAccountContract(signingKey)
|
|
224
|
+
: type === 'ecdsasecp256r1'
|
|
225
|
+
? new EcdsaRAccountContract(signingKey.toBuffer())
|
|
226
|
+
: new EcdsaKAccountContract(signingKey.toBuffer());
|
|
227
|
+
return await this.materializeAccount(secretKey, salt, contract);
|
|
189
228
|
}
|
|
190
229
|
case 'ecdsasecp256r1ssh': {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
publicSigningKey = await this.db.retrieveAccountMetadata(address, 'publicSigningKey');
|
|
194
|
-
} else if (publicKey) {
|
|
195
|
-
const identities = await getIdentities();
|
|
196
|
-
const foundIdentity = identities.find(
|
|
197
|
-
identity => identity.type === 'ecdsa-sha2-nistp256' && identity.publicKey === publicKey,
|
|
198
|
-
);
|
|
199
|
-
if (!foundIdentity) {
|
|
200
|
-
throw new Error(`Identity for public key ${publicKey} not found in the SSH agent`);
|
|
201
|
-
}
|
|
202
|
-
publicSigningKey = extractECDSAPublicKeyFromBase64String(foundIdentity.publicKey);
|
|
203
|
-
} else {
|
|
204
|
-
throw new Error('Public key must be provided for ECDSA SSH account');
|
|
230
|
+
if (!secretKey || !publicSigningKey) {
|
|
231
|
+
throw new Error('Cannot build SSH account without secret key and public signing key');
|
|
205
232
|
}
|
|
206
|
-
|
|
207
|
-
break;
|
|
233
|
+
return await this.materializeAccount(secretKey, salt, new EcdsaRSSHAccountContract(publicSigningKey));
|
|
208
234
|
}
|
|
209
235
|
default: {
|
|
210
236
|
throw new Error(`Unsupported account type: ${type}`);
|
|
211
237
|
}
|
|
212
238
|
}
|
|
239
|
+
}
|
|
213
240
|
|
|
214
|
-
|
|
241
|
+
private async resolveSshPublicSigningKey(publicKey: string | undefined): Promise<Buffer> {
|
|
242
|
+
if (!publicKey) {
|
|
243
|
+
throw new Error('Public key must be provided for ECDSA SSH account');
|
|
244
|
+
}
|
|
245
|
+
const identities = await getIdentities();
|
|
246
|
+
const foundIdentity = identities.find(
|
|
247
|
+
identity => identity.type === 'ecdsa-sha2-nistp256' && identity.publicKey === publicKey,
|
|
248
|
+
);
|
|
249
|
+
if (!foundIdentity) {
|
|
250
|
+
throw new Error(`Identity for public key ${publicKey} not found in the SSH agent`);
|
|
251
|
+
}
|
|
252
|
+
return extractECDSAPublicKeyFromBase64String(foundIdentity.publicKey);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private async materializeAccount(secret: Fr, salt: Fr, contract: AccountContract): Promise<AccountManager> {
|
|
256
|
+
const accountManager = await AccountManager.create(this, secret, contract, { salt });
|
|
257
|
+
|
|
258
|
+
const instance = accountManager.getInstance();
|
|
259
|
+
const artifact = await contract.getContractArtifact();
|
|
260
|
+
|
|
261
|
+
await this.registerContract(instance, artifact, secret);
|
|
262
|
+
this.accountCache.set(accountManager.address.toString(), await accountManager.getAccount());
|
|
263
|
+
|
|
264
|
+
// Initializerless accounts have no deployment tx; their address commits to the signing public key
|
|
265
|
+
// (via the contract's immutablesHash, resolved by AccountManager.create) and the constructor's
|
|
266
|
+
// storage writes are materialized locally via a simulated "store" call here.
|
|
267
|
+
if (contract instanceof SchnorrInitializerlessAccountContract) {
|
|
268
|
+
const constructorAbi = artifact.functions.find(f => f.name === 'constructor');
|
|
269
|
+
if (!constructorAbi) {
|
|
270
|
+
throw new Error('Could not create SchnorrInitializerlessAccount: constructor ABI not found');
|
|
271
|
+
}
|
|
272
|
+
const { x, y } = await contract.getSigningPublicKey();
|
|
273
|
+
const storeCall = new ContractFunctionInteraction(this, instance.address, constructorAbi, [x, y]);
|
|
274
|
+
await storeCall.simulate({ from: instance.address });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return accountManager;
|
|
215
278
|
}
|
|
216
279
|
|
|
217
280
|
/**
|
|
@@ -229,7 +292,9 @@ export class CLIWallet extends BaseWallet {
|
|
|
229
292
|
}
|
|
230
293
|
const { type } = await this.db!.retrieveAccount(address);
|
|
231
294
|
const stubAccount =
|
|
232
|
-
type === 'schnorr'
|
|
295
|
+
type === 'schnorr' || type === 'schnorr_initializerless'
|
|
296
|
+
? createStubSchnorrAccount(originalAddress)
|
|
297
|
+
: createStubEcdsaAccount(originalAddress);
|
|
233
298
|
const stubClassId = this.stubClassIds.get(type);
|
|
234
299
|
if (!stubClassId) {
|
|
235
300
|
throw new Error(
|
|
@@ -240,24 +305,6 @@ export class CLIWallet extends BaseWallet {
|
|
|
240
305
|
return { account: stubAccount, instance };
|
|
241
306
|
}
|
|
242
307
|
|
|
243
|
-
override async simulateTx(
|
|
244
|
-
executionPayload: ExecutionPayload,
|
|
245
|
-
opts: SimulateOptions,
|
|
246
|
-
): Promise<TxSimulationResultWithAppOffset> {
|
|
247
|
-
const simulationResults = await super.simulateTx(executionPayload, opts);
|
|
248
|
-
|
|
249
|
-
if (opts.fee?.estimateGas) {
|
|
250
|
-
const feeOptions = await this.completeFeeOptions({
|
|
251
|
-
from: opts.from,
|
|
252
|
-
feePayer: executionPayload.feePayer,
|
|
253
|
-
gasSettings: opts.fee?.gasSettings,
|
|
254
|
-
});
|
|
255
|
-
const limits = getGasLimits(simulationResults, opts.fee?.estimatedGasPadding);
|
|
256
|
-
printGasEstimates(feeOptions, limits, this.userLog);
|
|
257
|
-
}
|
|
258
|
-
return simulationResults;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
308
|
/**
|
|
262
309
|
* Uses a stub account for kernelless simulation, bypassing real account authorization.
|
|
263
310
|
* Uses DefaultEntrypoint directly for NO_FROM transactions.
|
|
@@ -267,7 +314,7 @@ export class CLIWallet extends BaseWallet {
|
|
|
267
314
|
opts: SimulateViaEntrypointOptions,
|
|
268
315
|
): Promise<TxSimulationResultWithAppOffset> {
|
|
269
316
|
const { from, feeOptions, additionalScopes, sendMessagesAs } = opts;
|
|
270
|
-
const scopes = this.scopesFrom(from, additionalScopes);
|
|
317
|
+
const scopes = this.scopesFrom(from, additionalScopes ?? [], sendMessagesAs);
|
|
271
318
|
const feeExecutionPayload = await feeOptions.walletFeePaymentMethod?.getExecutionPayload();
|
|
272
319
|
const finalExecutionPayload = feeExecutionPayload
|
|
273
320
|
? mergeExecutionPayloads([feeExecutionPayload, executionPayload])
|