Skip to content

Commit ce596ea

Browse files
authored
feat: cli-wallet (#21757)
A few changes to the cli-wallet to make it easier to use for testing deployed networks: - adds an optional salt parameter to `create-account`. This is so I can import a bot account which already has FJ - adds a command to quickly check FJ balance - all commands that send txs now take a `--wait-for-status <checkpointed|proposed>` optional flag (default: proposed) to control how long we wait for txs.
2 parents ce26a17 + f10b8f0 commit ce596ea

6 files changed

Lines changed: 172 additions & 57 deletions

File tree

yarn-project/cli-wallet/src/cmds/create_account.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { NO_FROM } from '@aztec/aztec.js/account';
22
import { AztecAddress } from '@aztec/aztec.js/addresses';
33
import { NO_WAIT } from '@aztec/aztec.js/contracts';
4-
import type { AztecNode } from '@aztec/aztec.js/node';
4+
import { type AztecNode, waitForTx } from '@aztec/aztec.js/node';
55
import type { DeployAccountOptions } from '@aztec/aztec.js/wallet';
66
import { prettyPrintJSON } from '@aztec/cli/cli-utils';
77
import { Fr } from '@aztec/foundation/curves/bn254';
88
import type { LogFn, Logger } from '@aztec/foundation/log';
9-
import type { TxHash, TxReceipt } from '@aztec/stdlib/tx';
9+
import { type TxHash, type TxReceipt, TxStatus } from '@aztec/stdlib/tx';
1010

1111
import { DEFAULT_TX_TIMEOUT_S } from '../utils/cli_wallet_and_node_wrapper.js';
1212
import type { AccountType } from '../utils/constants.js';
@@ -19,6 +19,7 @@ export async function createAccount(
1919
aztecNode: AztecNode,
2020
accountType: AccountType,
2121
secretKey: Fr | undefined,
22+
salt: Fr | undefined,
2223
publicKey: string | undefined,
2324
alias: string | undefined,
2425
deployer: AztecAddress | undefined,
@@ -28,6 +29,7 @@ export async function createAccount(
2829
registerClass: boolean,
2930
wait: boolean,
3031
feeOpts: CLIFeeArgs,
32+
waitForStatus: TxStatus,
3133
json: boolean,
3234
verbose: boolean,
3335
debugLogger: Logger,
@@ -39,10 +41,10 @@ export async function createAccount(
3941
undefined /* address, we don't have it yet */,
4042
secretKey,
4143
accountType,
42-
Fr.ZERO,
44+
salt,
4345
publicKey,
4446
);
45-
const { salt } = account.getInstance();
47+
const instanceSalt = account.getInstance().salt;
4648
const { address, publicKeys, partialAddress } = await account.getCompleteAddress();
4749

4850
const out: Record<string, any> = {};
@@ -53,7 +55,7 @@ export async function createAccount(
5355
out.secretKey = secretKey;
5456
}
5557
out.partialAddress = partialAddress;
56-
out.salt = salt;
58+
out.salt = instanceSalt;
5759
out.initHash = account.getInstance().initializationHash;
5860
} else {
5961
log(`\nNew account:\n`);
@@ -63,7 +65,7 @@ export async function createAccount(
6365
log(`Secret key: ${secretKey.toString()}`);
6466
}
6567
log(`Partial address: ${partialAddress.toString()}`);
66-
log(`Salt: ${salt.toString()}`);
68+
log(`Salt: ${instanceSalt.toString()}`);
6769
log(`Init hash: ${account.getInstance().initializationHash.toString()}`);
6870
}
6971

@@ -83,6 +85,7 @@ export async function createAccount(
8385
fee: { paymentMethod, gasSettings },
8486
};
8587

88+
const localStart = performance.now();
8689
const deployMethod = await account.getDeployMethod();
8790
const sim = await deployMethod.simulate({
8891
...deployAccountOpts,
@@ -122,19 +125,24 @@ export async function createAccount(
122125
}
123126
: undefined,
124127
};
128+
129+
({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT }));
130+
const localTimeMs = performance.now() - localStart;
131+
125132
if (wait) {
126-
const { receipt } = await deployMethod.send({
127-
...sendOpts,
128-
wait: { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true },
129-
});
130-
txReceipt = receipt;
131-
txHash = receipt.txHash;
133+
const nodeStart = performance.now();
134+
txReceipt = await waitForTx(aztecNode, txHash, { timeout: DEFAULT_TX_TIMEOUT_S, waitForStatus });
135+
const nodeTimeMs = performance.now() - nodeStart;
136+
132137
out.txReceipt = {
133138
status: txReceipt.status,
134139
transactionFee: txReceipt.transactionFee,
135140
};
136-
} else {
137-
({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT }));
141+
142+
if (!json) {
143+
log(` Local processing time: ${(localTimeMs / 1000).toFixed(1)}s`);
144+
log(` Node inclusion time: ${(nodeTimeMs / 1000).toFixed(1)}s`);
145+
}
138146
}
139147
debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`);
140148
out.txHash = txHash;
@@ -152,5 +160,5 @@ export async function createAccount(
152160
}
153161
}
154162

155-
return { alias, address, secretKey, salt };
163+
return { alias, address, secretKey, salt: instanceSalt };
156164
}

yarn-project/cli-wallet/src/cmds/deploy.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import type { DeployOptions } from '@aztec/aztec.js/contracts';
44
import { NO_WAIT } from '@aztec/aztec.js/contracts';
55
import { ContractDeployer } from '@aztec/aztec.js/deployment';
66
import { Fr } from '@aztec/aztec.js/fields';
7-
import type { AztecNode } from '@aztec/aztec.js/node';
7+
import { type AztecNode, waitForTx } from '@aztec/aztec.js/node';
88
import { encodeArgs, getContractArtifact, prettyPrintJSON } from '@aztec/cli/utils';
99
import type { LogFn, Logger } from '@aztec/foundation/log';
1010
import { getAllFunctionAbis, getInitializer } from '@aztec/stdlib/abi';
1111
import { PublicKeys } from '@aztec/stdlib/keys';
12+
import type { TxStatus } from '@aztec/stdlib/tx';
1213

1314
import { DEFAULT_TX_TIMEOUT_S } from '../utils/cli_wallet_and_node_wrapper.js';
1415
import { CLIFeeArgs } from '../utils/options/fees.js';
@@ -30,6 +31,7 @@ export async function deploy(
3031
skipInitialization: boolean | undefined,
3132
wait: boolean,
3233
feeOpts: CLIFeeArgs,
34+
waitForStatus: TxStatus,
3335
verbose: boolean,
3436
timeout: number = DEFAULT_TX_TIMEOUT_S,
3537
debugLogger: Logger,
@@ -60,7 +62,7 @@ export async function deploy(
6062
debugLogger.debug(`Encoded arguments: ${args.join(', ')}`);
6163
}
6264

63-
const deploy = contractDeployer.deploy(...args);
65+
const deployInteraction = contractDeployer.deploy(...args);
6466
const { paymentMethod, gasSettings } = await feeOpts.toUserFeeOptions(node, wallet, deployer);
6567
const deployOpts: DeployOptions = {
6668
fee: { gasSettings, paymentMethod },
@@ -72,7 +74,8 @@ export async function deploy(
7274
skipInstancePublication,
7375
};
7476

75-
const sim = await deploy.simulate({
77+
const localStart = performance.now();
78+
const sim = await deployInteraction.simulate({
7679
...deployOpts,
7780
fee: { ...deployOpts.fee, estimateGas: true },
7881
});
@@ -98,14 +101,18 @@ export async function deploy(
98101
printProfileResult(stats, log);
99102
}
100103

101-
const { address, partialAddress } = deploy;
102-
const instance = await deploy.getInstance();
104+
const { address, partialAddress } = deployInteraction;
105+
const instance = await deployInteraction.getInstance();
106+
107+
const { txHash } = await deployInteraction.send({ ...deployOpts, wait: NO_WAIT });
108+
const localTimeMs = performance.now() - localStart;
109+
debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`);
110+
out.hash = txHash;
103111

104112
if (wait) {
105-
const { receipt } = await deploy.send({ ...deployOpts, wait: { timeout, returnReceipt: true } });
106-
const txHash = receipt.txHash;
107-
debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`);
108-
out.hash = txHash;
113+
const nodeStart = performance.now();
114+
const receipt = await waitForTx(node, txHash, { timeout, waitForStatus });
115+
const nodeTimeMs = performance.now() - nodeStart;
109116

110117
if (!json) {
111118
log(`Contract deployed at ${address?.toString()}`);
@@ -115,6 +122,8 @@ export async function deploy(
115122
log(`Deployment salt: ${salt.toString()}`);
116123
log(`Deployer: ${instance.deployer.toString()}`);
117124
log(`Transaction fee: ${receipt.transactionFee?.toString()}`);
125+
log(` Local processing time: ${(localTimeMs / 1000).toFixed(1)}s`);
126+
log(` Node inclusion time: ${(nodeTimeMs / 1000).toFixed(1)}s`);
118127
} else {
119128
out.contract = {
120129
address: address?.toString(),
@@ -125,10 +134,6 @@ export async function deploy(
125134
};
126135
}
127136
} else {
128-
const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT });
129-
debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`);
130-
out.hash = txHash;
131-
132137
if (!json) {
133138
log(`Contract deployed at ${address?.toString()}`);
134139
log(`Contract partial address ${(await partialAddress)?.toString()}`);
@@ -149,5 +154,5 @@ export async function deploy(
149154
if (json) {
150155
log(prettyPrintJSON(out));
151156
}
152-
return deploy.address;
157+
return deployInteraction.address;
153158
}

yarn-project/cli-wallet/src/cmds/deploy_account.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { NO_FROM } from '@aztec/aztec.js/account';
22
import { AztecAddress } from '@aztec/aztec.js/addresses';
33
import { NO_WAIT } from '@aztec/aztec.js/contracts';
4-
import type { AztecNode } from '@aztec/aztec.js/node';
4+
import { type AztecNode, waitForTx } from '@aztec/aztec.js/node';
55
import type { DeployAccountOptions } from '@aztec/aztec.js/wallet';
66
import { prettyPrintJSON } from '@aztec/cli/cli-utils';
77
import type { LogFn, Logger } from '@aztec/foundation/log';
8-
import type { TxHash, TxReceipt } from '@aztec/stdlib/tx';
8+
import type { TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
99

1010
import { DEFAULT_TX_TIMEOUT_S } from '../utils/cli_wallet_and_node_wrapper.js';
1111
import type { CLIFeeArgs } from '../utils/options/fees.js';
@@ -22,6 +22,7 @@ export async function deployAccount(
2222
publicDeploy: boolean,
2323
skipInitialization: boolean,
2424
feeOpts: CLIFeeArgs,
25+
waitForStatus: TxStatus,
2526
json: boolean,
2627
verbose: boolean,
2728
debugLogger: Logger,
@@ -63,6 +64,7 @@ export async function deployAccount(
6364
fee: { paymentMethod, gasSettings },
6465
};
6566

67+
const localStart = performance.now();
6668
const deployMethod = await account.getDeployMethod();
6769
const sim = await deployMethod.simulate({
6870
...deployAccountOpts,
@@ -102,19 +104,24 @@ export async function deployAccount(
102104
}
103105
: undefined,
104106
};
107+
108+
({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT }));
109+
const localTimeMs = performance.now() - localStart;
110+
105111
if (wait) {
106-
const { receipt } = await deployMethod.send({
107-
...sendOpts,
108-
wait: { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true },
109-
});
110-
txReceipt = receipt;
111-
txHash = receipt.txHash;
112+
const nodeStart = performance.now();
113+
txReceipt = await waitForTx(aztecNode, txHash, { timeout: DEFAULT_TX_TIMEOUT_S, waitForStatus });
114+
const nodeTimeMs = performance.now() - nodeStart;
115+
112116
out.txReceipt = {
113117
status: txReceipt.status,
114118
transactionFee: txReceipt.transactionFee,
115119
};
116-
} else {
117-
({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT }));
120+
121+
if (!json) {
122+
log(` Local processing time: ${(localTimeMs / 1000).toFixed(1)}s`);
123+
log(` Node inclusion time: ${(nodeTimeMs / 1000).toFixed(1)}s`);
124+
}
118125
}
119126
debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`);
120127
out.txHash = txHash;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { AztecAddress } from '@aztec/aztec.js/addresses';
2+
import type { AztecNode } from '@aztec/aztec.js/node';
3+
import { getFeeJuiceBalance } from '@aztec/aztec.js/utils';
4+
import { prettyPrintJSON } from '@aztec/cli/cli-utils';
5+
import type { LogFn } from '@aztec/foundation/log';
6+
7+
const FEE_JUICE_DECIMALS = 18;
8+
const FEE_JUICE_UNIT = 10n ** BigInt(FEE_JUICE_DECIMALS);
9+
10+
/** Formats a raw FeeJuice balance (18 decimals) for human-readable display. */
11+
function formatFeeJuice(raw: bigint, exact: boolean): string {
12+
const whole = raw / FEE_JUICE_UNIT;
13+
const fractional = raw % FEE_JUICE_UNIT;
14+
const fracStr = fractional.toString().padStart(FEE_JUICE_DECIMALS, '0');
15+
if (exact) {
16+
return `${whole}.${fracStr} FJ`;
17+
}
18+
if (fractional === 0n) {
19+
return `${whole} FJ`;
20+
}
21+
return `${whole}.${fracStr.replace(/0+$/, '')} FJ`;
22+
}
23+
24+
export async function getFeeJuiceBalanceCmd(
25+
node: AztecNode,
26+
address: AztecAddress,
27+
json: boolean,
28+
exact: boolean,
29+
log: LogFn,
30+
) {
31+
const balance = await getFeeJuiceBalance(address, node);
32+
if (json) {
33+
log(prettyPrintJSON({ address: address.toString(), balance: balance.toString() }));
34+
} else {
35+
log(`Fee Juice balance for ${address.toString()}: ${formatFeeJuice(balance, exact)}`);
36+
}
37+
}

0 commit comments

Comments
 (0)