@aztec/cli 0.1.0-alpha13
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/.eslintrc.cjs +1 -0
- package/.tsbuildinfo +1 -0
- package/README.md +220 -0
- package/dest/cli_encoder.d.ts +15 -0
- package/dest/cli_encoder.d.ts.map +1 -0
- package/dest/cli_encoder.js +87 -0
- package/dest/index.d.ts +3 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +239 -0
- package/dest/utils.d.ts +34 -0
- package/dest/utils.d.ts.map +1 -0
- package/dest/utils.js +70 -0
- package/package.json +14 -0
- package/src/cli_encoder.ts +82 -0
- package/src/index.ts +287 -0
- package/src/utils.ts +86 -0
- package/tsconfig.json +21 -0
package/dest/utils.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { createEthereumChain, deployL1Contracts } from '@aztec/ethereum';
|
|
4
|
+
import { AztecAddress } from '@aztec/aztec.js';
|
|
5
|
+
import { encodeArgs } from './cli_encoder.js';
|
|
6
|
+
/**
|
|
7
|
+
* Function to execute the 'deployRollupContracts' command.
|
|
8
|
+
* @param rpcUrl - The RPC URL of the ethereum node.
|
|
9
|
+
* @param apiKey - The api key of the ethereum node endpoint.
|
|
10
|
+
* @param privateKey - The private key to be used in contract deployment.
|
|
11
|
+
* @param mnemonic - The mnemonic to be used in contract deployment.
|
|
12
|
+
*/
|
|
13
|
+
export async function deployAztecContracts(rpcUrl, apiKey, privateKey, mnemonic, debugLogger) {
|
|
14
|
+
const account = !privateKey ? mnemonicToAccount(mnemonic) : privateKeyToAccount(`0x${privateKey}`);
|
|
15
|
+
const chain = createEthereumChain(rpcUrl, apiKey);
|
|
16
|
+
await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Reads a file and converts it to an Aztec Contract ABI.
|
|
20
|
+
* @param fileDir - The directory of the compiled contract ABI.
|
|
21
|
+
* @returns The parsed ContractABI.
|
|
22
|
+
*/
|
|
23
|
+
export function getContractAbi(fileDir, log) {
|
|
24
|
+
const contents = fs.readFileSync(fileDir, 'utf8');
|
|
25
|
+
let contractAbi;
|
|
26
|
+
try {
|
|
27
|
+
contractAbi = JSON.parse(contents);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
log('Invalid file used. Please try again.');
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
return contractAbi;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Performs necessary checks, conversions & operations to call a contract fn from the CLI.
|
|
37
|
+
* @param contractFile - Directory of the compiled contract ABI.
|
|
38
|
+
* @param _contractAddress - Aztec Address of the contract.
|
|
39
|
+
* @param functionName - Name of the function to be called.
|
|
40
|
+
* @param _from - The caller's address.
|
|
41
|
+
* @param _functionArgs - Arguments to call the function with.
|
|
42
|
+
* @param log - Logger instance that will output to the CLI
|
|
43
|
+
* @returns Formatted contract address, function arguments and caller's aztec address.
|
|
44
|
+
*/
|
|
45
|
+
export function prepTx(contractFile, _contractAddress, functionName, _from, _functionArgs, log) {
|
|
46
|
+
let contractAddress;
|
|
47
|
+
try {
|
|
48
|
+
contractAddress = AztecAddress.fromString(_contractAddress);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error(`Unable to parse contract address ${_contractAddress}.`);
|
|
52
|
+
}
|
|
53
|
+
const contractAbi = getContractAbi(contractFile, log);
|
|
54
|
+
const functionAbi = contractAbi.functions.find(({ name }) => name === functionName);
|
|
55
|
+
if (!functionAbi) {
|
|
56
|
+
throw new Error(`Function ${functionName} not found on contract ABI.`);
|
|
57
|
+
}
|
|
58
|
+
const functionArgs = encodeArgs(_functionArgs, functionAbi.parameters);
|
|
59
|
+
let from;
|
|
60
|
+
if (_from) {
|
|
61
|
+
try {
|
|
62
|
+
from = AztecAddress.fromString(_from);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new Error(`Unable to parse caller address ${_from}.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { contractAddress, functionArgs, from, contractAbi };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGlCQUFpQixFQUFFLG1CQUFtQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUd6RSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRTlDOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsb0JBQW9CLENBQ3hDLE1BQWMsRUFDZCxNQUFjLEVBQ2QsVUFBa0IsRUFDbEIsUUFBZ0IsRUFDaEIsV0FBd0I7SUFFeEIsTUFBTSxPQUFPLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLFFBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLFVBQVUsRUFBRSxDQUFDLENBQUM7SUFDcEcsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2xELE1BQU0saUJBQWlCLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVMsRUFBRSxXQUFXLENBQUMsQ0FBQztBQUMvRSxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxjQUFjLENBQUMsT0FBZSxFQUFFLEdBQVc7SUFDekQsTUFBTSxRQUFRLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDbEQsSUFBSSxXQUF3QixDQUFDO0lBQzdCLElBQUk7UUFDRixXQUFXLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQWdCLENBQUM7S0FDbkQ7SUFBQyxPQUFPLEdBQUcsRUFBRTtRQUNaLEdBQUcsQ0FBQyxzQ0FBc0MsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sR0FBRyxDQUFDO0tBQ1g7SUFDRCxPQUFPLFdBQVcsQ0FBQztBQUNyQixDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxVQUFVLE1BQU0sQ0FDcEIsWUFBb0IsRUFDcEIsZ0JBQXdCLEVBQ3hCLFlBQW9CLEVBQ3BCLEtBQWEsRUFDYixhQUF1QixFQUN2QixHQUFXO0lBRVgsSUFBSSxlQUFlLENBQUM7SUFDcEIsSUFBSTtRQUNGLGVBQWUsR0FBRyxZQUFZLENBQUMsVUFBVSxDQUFDLGdCQUFnQixDQUFDLENBQUM7S0FDN0Q7SUFBQyxNQUFNO1FBQ04sTUFBTSxJQUFJLEtBQUssQ0FBQyxvQ0FBb0MsZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDO0tBQzFFO0lBQ0QsTUFBTSxXQUFXLEdBQUcsY0FBYyxDQUFDLFlBQVksRUFBRSxHQUFHLENBQUMsQ0FBQztJQUN0RCxNQUFNLFdBQVcsR0FBRyxXQUFXLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDLElBQUksS0FBSyxZQUFZLENBQUMsQ0FBQztJQUNwRixJQUFJLENBQUMsV0FBVyxFQUFFO1FBQ2hCLE1BQU0sSUFBSSxLQUFLLENBQUMsWUFBWSxZQUFZLDZCQUE2QixDQUFDLENBQUM7S0FDeEU7SUFFRCxNQUFNLFlBQVksR0FBRyxVQUFVLENBQUMsYUFBYSxFQUFFLFdBQVcsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN2RSxJQUFJLElBQUksQ0FBQztJQUNULElBQUksS0FBSyxFQUFFO1FBQ1QsSUFBSTtZQUNGLElBQUksR0FBRyxZQUFZLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDO1NBQ3ZDO1FBQUMsTUFBTTtZQUNOLE1BQU0sSUFBSSxLQUFLLENBQUMsa0NBQWtDLEtBQUssR0FBRyxDQUFDLENBQUM7U0FDN0Q7S0FDRjtJQUVELE9BQU8sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsQ0FBQztBQUM5RCxDQUFDIn0=
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aztec/cli",
|
|
3
|
+
"version": "0.1.0-alpha13",
|
|
4
|
+
"exports": "./dest/index.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@aztec/aztec.js": "0.1.0-alpha13",
|
|
8
|
+
"@aztec/ethereum": "0.1.0-alpha13",
|
|
9
|
+
"@aztec/foundation": "0.1.0-alpha13",
|
|
10
|
+
"commander": "^9.0.0",
|
|
11
|
+
"tslib": "^2.4.0",
|
|
12
|
+
"viem": "^1.2.5"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Fr } from '@aztec/aztec.js';
|
|
2
|
+
import { ABIParameter, ABIType, StructType } from '@aztec/foundation/abi';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parses a hex string into an ABI struct type.
|
|
6
|
+
* @param str - The encoded hex string.
|
|
7
|
+
* @param abiType - The ABI Struct type.
|
|
8
|
+
* @returns An object in the ABI struct type's format.
|
|
9
|
+
*/
|
|
10
|
+
export function parseStructString(str: string, abiType: StructType) {
|
|
11
|
+
// Assing string bytes to struct fields.
|
|
12
|
+
const buf = Buffer.from(str.replace(/^0x/i, ''), 'hex');
|
|
13
|
+
const struct: any = {};
|
|
14
|
+
let byteIndex = 0;
|
|
15
|
+
let argIndex = 0;
|
|
16
|
+
while (byteIndex < buf.length) {
|
|
17
|
+
const { name } = abiType.fields[argIndex];
|
|
18
|
+
struct[name] = Fr.fromBuffer(buf.subarray(byteIndex, byteIndex + 32));
|
|
19
|
+
byteIndex += 32;
|
|
20
|
+
argIndex += 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return struct;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Helper function to encode CLI string args to an appropriate JS type.
|
|
28
|
+
* @param arg - The CLI argument.
|
|
29
|
+
* @param abiType - The type as described by the contract's ABI.
|
|
30
|
+
* @returns The encoded argument.
|
|
31
|
+
*/
|
|
32
|
+
function encodeArg(arg: string, abiType: ABIType): any {
|
|
33
|
+
const { kind } = abiType;
|
|
34
|
+
if (kind === 'field' || kind === 'integer') {
|
|
35
|
+
return BigInt(arg);
|
|
36
|
+
} else if (kind === 'boolean') {
|
|
37
|
+
if (arg === 'true') return true;
|
|
38
|
+
if (arg === 'false') return false;
|
|
39
|
+
} else if (kind === 'array') {
|
|
40
|
+
let arr;
|
|
41
|
+
try {
|
|
42
|
+
arr = JSON.parse(arg);
|
|
43
|
+
if (!Array.isArray(arr)) throw Error();
|
|
44
|
+
for (let i = 0; i < abiType.length; i += 1) {
|
|
45
|
+
return encodeArg(arg[i], abiType.type);
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error(`Unable to parse arg ${arg} as array`);
|
|
49
|
+
}
|
|
50
|
+
} else if (kind === 'struct') {
|
|
51
|
+
// check if input is encoded long string
|
|
52
|
+
if (arg.startsWith('0x')) {
|
|
53
|
+
return parseStructString(arg, abiType);
|
|
54
|
+
}
|
|
55
|
+
let obj;
|
|
56
|
+
try {
|
|
57
|
+
obj = JSON.parse(arg);
|
|
58
|
+
if (Array.isArray(obj)) throw Error();
|
|
59
|
+
const res = [];
|
|
60
|
+
for (const field of abiType.fields) {
|
|
61
|
+
res.push(encodeArg(obj[field.name], field.type));
|
|
62
|
+
}
|
|
63
|
+
return res;
|
|
64
|
+
} catch {
|
|
65
|
+
throw new Error(`Unable to parse arg ${arg} as struct`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Tries to encode function args to their equivalent TS type.
|
|
72
|
+
* @param args - An array of function's / constructor's args.
|
|
73
|
+
* @returns The encoded array.
|
|
74
|
+
*/
|
|
75
|
+
export function encodeArgs(args: any[], params: ABIParameter[]) {
|
|
76
|
+
return args
|
|
77
|
+
.map((arg: any, index) => {
|
|
78
|
+
const paramType = params[index].type;
|
|
79
|
+
return encodeArg(arg, paramType);
|
|
80
|
+
})
|
|
81
|
+
.flat();
|
|
82
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
5
|
+
import {
|
|
6
|
+
AztecAddress,
|
|
7
|
+
Contract,
|
|
8
|
+
ContractDeployer,
|
|
9
|
+
Point,
|
|
10
|
+
TxHash,
|
|
11
|
+
createAccounts,
|
|
12
|
+
createAztecRpcClient,
|
|
13
|
+
pointToPublicKey,
|
|
14
|
+
} from '@aztec/aztec.js';
|
|
15
|
+
|
|
16
|
+
import { encodeArgs, parseStructString } from './cli_encoder.js';
|
|
17
|
+
import { deployAztecContracts, getContractAbi, prepTx } from './utils.js';
|
|
18
|
+
import { JsonStringify } from '@aztec/foundation/json-rpc';
|
|
19
|
+
import { StructType } from '@aztec/foundation/abi';
|
|
20
|
+
import { ContractData, L2BlockL2Logs } from '@aztec/types';
|
|
21
|
+
|
|
22
|
+
const debugLogger = createDebugLogger('aztec:cli');
|
|
23
|
+
const log = createLogger();
|
|
24
|
+
|
|
25
|
+
const program = new Command();
|
|
26
|
+
|
|
27
|
+
program.name('azti').description('CLI for interacting with Aztec.').version('0.1.0');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A placeholder for the Aztec-cli.
|
|
31
|
+
*/
|
|
32
|
+
async function main() {
|
|
33
|
+
program
|
|
34
|
+
.command('deploy-l1-contracts')
|
|
35
|
+
.argument(
|
|
36
|
+
'[rpcUrl]',
|
|
37
|
+
'Url of the ethereum host. Chain identifiers localhost and testnet can be used',
|
|
38
|
+
'http://localhost:8545',
|
|
39
|
+
)
|
|
40
|
+
.option('-a, --api-key <string>', 'Api key for the ethereum host', undefined)
|
|
41
|
+
.option('-p, --private-key <string>', 'The private key to use for deployment')
|
|
42
|
+
.option(
|
|
43
|
+
'-m, --mnemonic <string>',
|
|
44
|
+
'The mnemonic to use in deployment',
|
|
45
|
+
'test test test test test test test test test test test junk',
|
|
46
|
+
)
|
|
47
|
+
.action(async (rpcUrl: string, options) => {
|
|
48
|
+
await deployAztecContracts(rpcUrl, options.apiKey ?? '', options.privateKey, options.mnemonic, debugLogger);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
program
|
|
52
|
+
.command('deploy')
|
|
53
|
+
.argument('<contractAbi>', "A compiled Noir contract's ABI in JSON format", undefined)
|
|
54
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
55
|
+
.option('-k, --public-key <string>')
|
|
56
|
+
.option('-a, --constructor-args [args...]', 'Contract constructor arguments', [])
|
|
57
|
+
.action(async (contractFile: string, options: any) => {
|
|
58
|
+
const contractAbi = getContractAbi(contractFile, log);
|
|
59
|
+
const constructorAbi = contractAbi.functions.find(({ name }) => name === 'constructor');
|
|
60
|
+
|
|
61
|
+
const publicKey = Point.fromString(options.publicKey);
|
|
62
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
63
|
+
const deployer = new ContractDeployer(contractAbi, client);
|
|
64
|
+
|
|
65
|
+
const tx = deployer
|
|
66
|
+
.deploy(...encodeArgs(options.constructorArgs, constructorAbi!.parameters), pointToPublicKey(publicKey))
|
|
67
|
+
.send();
|
|
68
|
+
await tx.isMined();
|
|
69
|
+
const receipt = await tx.getReceipt();
|
|
70
|
+
log(`Contract deployed at ${receipt.contractAddress?.toString()}`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
program
|
|
74
|
+
.command('check-deploy')
|
|
75
|
+
.argument('<contractAddress>', 'An Aztec address to check if contract has been deployed to.')
|
|
76
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
77
|
+
.action(async (_contractAddress, options) => {
|
|
78
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
79
|
+
const address = AztecAddress.fromString(_contractAddress);
|
|
80
|
+
const isDeployed = await client.isContractDeployed(address);
|
|
81
|
+
log(isDeployed.toString());
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
program
|
|
85
|
+
.command('get-tx-receipt')
|
|
86
|
+
.argument('<txHash>', 'A TX hash to get the receipt for.')
|
|
87
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
88
|
+
.action(async (_txHash, options) => {
|
|
89
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
90
|
+
const txHash = TxHash.fromString(_txHash);
|
|
91
|
+
const receipt = await client.getTxReceipt(txHash);
|
|
92
|
+
if (!receipt) {
|
|
93
|
+
log(`No receipt found for tx hash ${_txHash}`);
|
|
94
|
+
} else {
|
|
95
|
+
log(`TX Receipt: \n${JsonStringify(receipt, true)}`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
program
|
|
100
|
+
.command('get-contract-data')
|
|
101
|
+
.argument('<contractAddress>', 'Aztec address of the contract.')
|
|
102
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
103
|
+
.option('-b, --include-bytecode', "Include the contract's public function bytecode, if any.")
|
|
104
|
+
.action(async (_contractAddress, options) => {
|
|
105
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
106
|
+
const address = AztecAddress.fromString(_contractAddress);
|
|
107
|
+
const contractDataOrInfo = options.includeBytecode
|
|
108
|
+
? await client.getContractData(address)
|
|
109
|
+
: await client.getContractInfo(address);
|
|
110
|
+
|
|
111
|
+
if (!contractDataOrInfo) {
|
|
112
|
+
log(`No contract data found at ${_contractAddress}`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
let contractData: ContractData;
|
|
116
|
+
|
|
117
|
+
if ('contractData' in contractDataOrInfo) {
|
|
118
|
+
contractData = contractDataOrInfo.contractData;
|
|
119
|
+
} else {
|
|
120
|
+
contractData = contractDataOrInfo;
|
|
121
|
+
}
|
|
122
|
+
log(`Contract Data: \nAddress: ${contractData.contractAddress.toString()}`);
|
|
123
|
+
log(`Portal: ${contractData.portalContractAddress.toString()}`);
|
|
124
|
+
if ('bytecode' in contractDataOrInfo) {
|
|
125
|
+
log(`Bytecode: ${contractDataOrInfo.bytecode}`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
program
|
|
130
|
+
.command('get-logs')
|
|
131
|
+
.argument('<from>', 'Block num start for getting logs.')
|
|
132
|
+
.argument('<take>', 'How many block logs to fetch.')
|
|
133
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
134
|
+
.action(async (_from, _take, options) => {
|
|
135
|
+
let from: number;
|
|
136
|
+
let take: number;
|
|
137
|
+
try {
|
|
138
|
+
from = parseInt(_from);
|
|
139
|
+
take = parseInt(_take);
|
|
140
|
+
} catch {
|
|
141
|
+
log(`Invalid integer value(s) passed: ${_from}, ${_take}`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
145
|
+
const logs = await client.getUnencryptedLogs(from, take);
|
|
146
|
+
if (!logs.length) {
|
|
147
|
+
log(`No logs found in blocks ${from} to ${from + take}`);
|
|
148
|
+
} else {
|
|
149
|
+
log('Logs found: \n');
|
|
150
|
+
L2BlockL2Logs.unrollLogs(logs).forEach(fnLog => log(`${fnLog.toString('ascii')}\n`));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// NOTE: This implementation should change soon but keeping it here for quick account creation.
|
|
155
|
+
program
|
|
156
|
+
.command('create-account')
|
|
157
|
+
.option('-k, --private-key', 'Private Key to use for the 1st account generation.')
|
|
158
|
+
.option('-n, --num-addresses <number>', 'Number of addresses the account can control')
|
|
159
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
160
|
+
.action(async options => {
|
|
161
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
162
|
+
const privateKey = options.privateKey && Buffer.from(options.privateKeystr.replace(/^0x/i, ''), 'hex');
|
|
163
|
+
const numAccounts = options.numAddresses ? parseInt(options.numAddresses) : 1;
|
|
164
|
+
const wallet = await createAccounts(client, privateKey, numAccounts);
|
|
165
|
+
const accounts = await wallet.getAccounts();
|
|
166
|
+
const pubKeys = await Promise.all(accounts.map(acc => wallet.getAccountPublicKey(acc)));
|
|
167
|
+
log(`Created account(s).`);
|
|
168
|
+
accounts.map((acc, i) => log(`\nAddress: ${acc.toString()}\nPublic Key: ${pubKeys[i].toString()}\n`));
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
program
|
|
172
|
+
.command('get-accounts')
|
|
173
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
174
|
+
.action(async (options: any) => {
|
|
175
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
176
|
+
const accounts = await client.getAccounts();
|
|
177
|
+
if (!accounts.length) {
|
|
178
|
+
log('No accounts found.');
|
|
179
|
+
} else {
|
|
180
|
+
log(`Accounts found: \n`);
|
|
181
|
+
accounts.forEach(acc => log(`${acc}\n`));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
program
|
|
186
|
+
.command('get-account-public-key')
|
|
187
|
+
.argument('<address>', 'The Aztec address to get the public key for')
|
|
188
|
+
.option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
189
|
+
.action(async (_address, options) => {
|
|
190
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
191
|
+
const address = AztecAddress.fromString(_address);
|
|
192
|
+
const pk = await client.getAccountPublicKey(address);
|
|
193
|
+
if (!pk) {
|
|
194
|
+
log(`Unkown account ${_address}`);
|
|
195
|
+
} else {
|
|
196
|
+
log(`Public Key: \n ${pk.toString()}`);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
program
|
|
201
|
+
.command('call-fn')
|
|
202
|
+
.argument('<contractAbi>', "The compiled contract's ABI in JSON format", undefined)
|
|
203
|
+
.argument('<contractAddress>', 'Address of the contract')
|
|
204
|
+
.argument('<functionName>', 'Name of Function to view')
|
|
205
|
+
.argument('[from]', 'The caller of the transaction', undefined)
|
|
206
|
+
.argument('[functionArgs...]', 'Function arguments', [])
|
|
207
|
+
.option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
208
|
+
.action(async (contractFile, _contractAddress, functionName, _from, _functionArgs, options) => {
|
|
209
|
+
const { contractAddress, functionArgs, from, contractAbi } = prepTx(
|
|
210
|
+
contractFile,
|
|
211
|
+
_contractAddress,
|
|
212
|
+
functionName,
|
|
213
|
+
_from,
|
|
214
|
+
_functionArgs,
|
|
215
|
+
log,
|
|
216
|
+
);
|
|
217
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
218
|
+
const wallet = await createAccounts(client);
|
|
219
|
+
const contract = new Contract(contractAddress, contractAbi, wallet);
|
|
220
|
+
const tx = contract.methods[functionName](...functionArgs).send({ from });
|
|
221
|
+
await tx.isMined();
|
|
222
|
+
log('TX has been mined');
|
|
223
|
+
const receipt = await tx.getReceipt();
|
|
224
|
+
log(`TX Hash: ${(await tx.getTxHash()).toString()}`);
|
|
225
|
+
log(`Block Num: ${receipt.blockNumber}`);
|
|
226
|
+
log(`Block Hash: ${receipt.blockHash?.toString('hex')}`);
|
|
227
|
+
log(`TX Status: ${receipt.status}`);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
program
|
|
231
|
+
.command('view-tx')
|
|
232
|
+
.argument('<contractAbi>', "The compiled contract's ABI in JSON format", undefined)
|
|
233
|
+
.argument('<contractAddress>', 'Address of the contract')
|
|
234
|
+
.argument('<functionName>', 'Name of Function to view')
|
|
235
|
+
.argument('[from]', 'The caller of the transaction', undefined)
|
|
236
|
+
.argument('[functionArgs...]', 'Function arguments', [])
|
|
237
|
+
.option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
238
|
+
.action(async (contractFile, _contractAddress, functionName, _from, _functionArgs, options) => {
|
|
239
|
+
const { contractAddress, functionArgs, from } = prepTx(
|
|
240
|
+
contractFile,
|
|
241
|
+
_contractAddress,
|
|
242
|
+
functionName,
|
|
243
|
+
_from,
|
|
244
|
+
_functionArgs,
|
|
245
|
+
log,
|
|
246
|
+
);
|
|
247
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
248
|
+
const result = await client.viewTx(functionName, functionArgs, contractAddress, from);
|
|
249
|
+
log('View TX returned result: ', JsonStringify(result, true));
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Helper for users to decode hex strings into structs if needed
|
|
253
|
+
program
|
|
254
|
+
.command('parse-parameter-struct')
|
|
255
|
+
.argument('<encodedString>', 'The encoded hex string')
|
|
256
|
+
.argument('<contractAbi>', "The compiled contract's ABI in JSON format")
|
|
257
|
+
.argument('<parameterName>', 'The name of the struct parameter to decode into')
|
|
258
|
+
.action((encodedString, contractFile, parameterName) => {
|
|
259
|
+
const contractAbi = getContractAbi(contractFile, log);
|
|
260
|
+
const parameterAbitype = contractAbi.functions
|
|
261
|
+
.map(({ parameters }) => parameters)
|
|
262
|
+
.flat()
|
|
263
|
+
.find(({ name, type }) => name === parameterName && type.kind === 'struct');
|
|
264
|
+
if (!parameterAbitype) {
|
|
265
|
+
log(`No struct parameter found with name ${parameterName}`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const data = parseStructString(encodedString, parameterAbitype.type as StructType);
|
|
269
|
+
log(`Struct Data: \n${JsonStringify(data, true)}`);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
program
|
|
273
|
+
.command('block-num')
|
|
274
|
+
.option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
|
|
275
|
+
.action(async (options: any) => {
|
|
276
|
+
const client = createAztecRpcClient(options.rpcUrl);
|
|
277
|
+
const num = await client.getBlockNum();
|
|
278
|
+
log(num);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
await program.parseAsync(process.argv);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
main().catch(err => {
|
|
285
|
+
log(`Error thrown: ${err}`);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
});
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { createEthereumChain, deployL1Contracts } from '@aztec/ethereum';
|
|
4
|
+
import { DebugLogger, Logger } from '@aztec/foundation/log';
|
|
5
|
+
import { ContractAbi } from '@aztec/foundation/abi';
|
|
6
|
+
import { AztecAddress } from '@aztec/aztec.js';
|
|
7
|
+
import { encodeArgs } from './cli_encoder.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Function to execute the 'deployRollupContracts' command.
|
|
11
|
+
* @param rpcUrl - The RPC URL of the ethereum node.
|
|
12
|
+
* @param apiKey - The api key of the ethereum node endpoint.
|
|
13
|
+
* @param privateKey - The private key to be used in contract deployment.
|
|
14
|
+
* @param mnemonic - The mnemonic to be used in contract deployment.
|
|
15
|
+
*/
|
|
16
|
+
export async function deployAztecContracts(
|
|
17
|
+
rpcUrl: string,
|
|
18
|
+
apiKey: string,
|
|
19
|
+
privateKey: string,
|
|
20
|
+
mnemonic: string,
|
|
21
|
+
debugLogger: DebugLogger,
|
|
22
|
+
) {
|
|
23
|
+
const account = !privateKey ? mnemonicToAccount(mnemonic!) : privateKeyToAccount(`0x${privateKey}`);
|
|
24
|
+
const chain = createEthereumChain(rpcUrl, apiKey);
|
|
25
|
+
await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Reads a file and converts it to an Aztec Contract ABI.
|
|
30
|
+
* @param fileDir - The directory of the compiled contract ABI.
|
|
31
|
+
* @returns The parsed ContractABI.
|
|
32
|
+
*/
|
|
33
|
+
export function getContractAbi(fileDir: string, log: Logger) {
|
|
34
|
+
const contents = fs.readFileSync(fileDir, 'utf8');
|
|
35
|
+
let contractAbi: ContractAbi;
|
|
36
|
+
try {
|
|
37
|
+
contractAbi = JSON.parse(contents) as ContractAbi;
|
|
38
|
+
} catch (err) {
|
|
39
|
+
log('Invalid file used. Please try again.');
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
return contractAbi;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Performs necessary checks, conversions & operations to call a contract fn from the CLI.
|
|
47
|
+
* @param contractFile - Directory of the compiled contract ABI.
|
|
48
|
+
* @param _contractAddress - Aztec Address of the contract.
|
|
49
|
+
* @param functionName - Name of the function to be called.
|
|
50
|
+
* @param _from - The caller's address.
|
|
51
|
+
* @param _functionArgs - Arguments to call the function with.
|
|
52
|
+
* @param log - Logger instance that will output to the CLI
|
|
53
|
+
* @returns Formatted contract address, function arguments and caller's aztec address.
|
|
54
|
+
*/
|
|
55
|
+
export function prepTx(
|
|
56
|
+
contractFile: string,
|
|
57
|
+
_contractAddress: string,
|
|
58
|
+
functionName: string,
|
|
59
|
+
_from: string,
|
|
60
|
+
_functionArgs: string[],
|
|
61
|
+
log: Logger,
|
|
62
|
+
) {
|
|
63
|
+
let contractAddress;
|
|
64
|
+
try {
|
|
65
|
+
contractAddress = AztecAddress.fromString(_contractAddress);
|
|
66
|
+
} catch {
|
|
67
|
+
throw new Error(`Unable to parse contract address ${_contractAddress}.`);
|
|
68
|
+
}
|
|
69
|
+
const contractAbi = getContractAbi(contractFile, log);
|
|
70
|
+
const functionAbi = contractAbi.functions.find(({ name }) => name === functionName);
|
|
71
|
+
if (!functionAbi) {
|
|
72
|
+
throw new Error(`Function ${functionName} not found on contract ABI.`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const functionArgs = encodeArgs(_functionArgs, functionAbi.parameters);
|
|
76
|
+
let from;
|
|
77
|
+
if (_from) {
|
|
78
|
+
try {
|
|
79
|
+
from = AztecAddress.fromString(_from);
|
|
80
|
+
} catch {
|
|
81
|
+
throw new Error(`Unable to parse caller address ${_from}.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { contractAddress, functionArgs, from, contractAbi };
|
|
86
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "..",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dest",
|
|
5
|
+
"rootDir": "src",
|
|
6
|
+
"tsBuildInfoFile": ".tsbuildinfo"
|
|
7
|
+
},
|
|
8
|
+
"references": [
|
|
9
|
+
{
|
|
10
|
+
"path": "../aztec.js"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"path": "../ethereum"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "../foundation"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"include": ["src"],
|
|
20
|
+
"exclude": ["contracts"]
|
|
21
|
+
}
|