Skip to content

Commit

Permalink
fix: loading salt into buffer in cli
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed Sep 22, 2023
1 parent 4992d5b commit 76d4177
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 12 deletions.
19 changes: 9 additions & 10 deletions yarn-project/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,14 @@ import {
getAbiFunction,
getContractAbi,
getExampleContractArtifacts,
getSaltFromHexString,
getTxSender,
prepTx,
stripLeadingHex,
} from './utils.js';

const accountCreationSalt = Fr.ZERO;

const stripLeadingHex = (hex: string) => {
if (hex.length > 2 && hex.startsWith('0x')) {
return hex.substring(2);
}
return hex;
};

const { ETHEREUM_HOST, AZTEC_RPC_HOST, PRIVATE_KEY, API_KEY } = process.env;

/**
Expand Down Expand Up @@ -160,14 +155,18 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
'-k, --public-key <string>',
'Optional encryption public key for this address. Set this value only if this contract is expected to receive private notes, which will be encrypted using this public key.',
)
.option('-s, --salt <string>', 'Optional deployment salt as a hex string for generating the deployment address.')
.option(
'-s, --salt <hex string>',
'Optional deployment salt as a hex string for generating the deployment address.',
getSaltFromHexString,
)
.action(async (abiPath, options: any) => {
const contractAbi = await getContractAbi(abiPath, log);
const constructorAbi = contractAbi.functions.find(({ name }) => name === 'constructor');

const client = await createCompatibleClient(options.rpcUrl, debugLogger);
const publicKey = options.publicKey ? Point.fromString(options.publicKey) : undefined;
const salt = options.salt ? Fr.fromBuffer(Buffer.from(stripLeadingHex(options.salt), 'hex')) : undefined;

const deployer = new ContractDeployer(contractAbi, client, publicKey);

const constructor = getAbiFunction(contractAbi, 'constructor');
Expand All @@ -176,7 +175,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
debugLogger(`Input arguments: ${options.args.map((x: any) => `"${x}"`).join(', ')}`);
const args = encodeArgs(options.args, constructorAbi!.parameters);
debugLogger(`Encoded arguments: ${args.join(', ')}`);
const tx = deployer.deploy(...args).send({ contractAddressSalt: salt });
const tx = deployer.deploy(...args).send({ contractAddressSalt: options.salt });
debugLogger(`Deploy tx sent with hash ${await tx.getTxHash()}`);
const deployed = await tx.wait();
log(`\nContract deployed at ${deployed.contractAddress!.toString()}\n`);
Expand Down
33 changes: 32 additions & 1 deletion yarn-project/cli/src/test/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { AztecAddress, Fr } from '@aztec/aztec.js';
import { AztecRPC, CompleteAddress } from '@aztec/types';

import { InvalidArgumentError } from 'commander';
import { MockProxy, mock } from 'jest-mock-extended';

import { encodeArgs } from '../encoding.js';
import { getTxSender } from '../utils.js';
import { getSaltFromHexString, getTxSender, stripLeadingHex } from '../utils.js';
import { mockContractAbi } from './mocks.js';

describe('CLI Utils', () => {
Expand Down Expand Up @@ -128,4 +129,34 @@ describe('CLI Utils', () => {
'Invalid value passed for integerParam. Could not parse foo as an integer.',
);
});

describe('stripLeadingHex', () => {
it.each([
['0x1', '1'],
['1', '1'],
['0x00', '00'],
['00', '00'],
])('removes optional leading hex', (hex, expected) => {
expect(stripLeadingHex(hex)).toEqual(expected);
});
});

describe('getSaltFromHex', () => {
it.each([
['0', Fr.ZERO],
['0x0', Fr.ZERO],
['00', Fr.ZERO],
['1', new Fr(1)],
['0x1', new Fr(1)],
['0x01', new Fr(1)],
['0xa', new Fr(0xa)],
['fff', new Fr(0xfff)],
])('correctly generates salt from a hex string', (hex, expected) => {
expect(getSaltFromHexString(hex)).toEqual(expected);
});

it.each(['foo', '', ' ', ' 0x1', '01foo', 'foo1', '0xfoo'])('throws an error for invalid hex strings', str => {
expect(() => getSaltFromHexString(str)).toThrow(InvalidArgumentError);
});
});
});
36 changes: 35 additions & 1 deletion yarn-project/cli/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { AztecAddress, AztecRPC } from '@aztec/aztec.js';
import { AztecAddress, AztecRPC, Fr } from '@aztec/aztec.js';
import { createEthereumChain, deployL1Contracts } from '@aztec/ethereum';
import { ContractAbi } from '@aztec/foundation/abi';
import { DebugLogger, LogFn } from '@aztec/foundation/log';

import { InvalidArgumentError } from 'commander';
import fs from 'fs';
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';

Expand Down Expand Up @@ -141,3 +142,36 @@ export async function prepTx(

return { contractAddress, functionArgs, contractAbi };
}

/**
* Removes the leading 0x from a hex string. If no leading 0x is found the string is returned unchanged.
* @param hex - A hex string
* @returns A new string with leading 0x removed
*/
export const stripLeadingHex = (hex: string) => {
if (hex.length > 2 && hex.startsWith('0x')) {
return hex.substring(2);
}
return hex;
};

/**
* Parses a hex encoded string to an Fr integer to be used as salt
* @param str - Hex encoded string
* @returns A integer to be used as salt
*/
export function getSaltFromHexString(str: string): Fr {
const hex = stripLeadingHex(str);

// ensure it's a hex string
if (!hex.match(/^[0-9a-f]+$/i)) {
throw new InvalidArgumentError('Invalid hex string');
}

// pad it so that we may read it as a buffer.
// Buffer needs _exactly_ two hex characters per byte
const padded = hex.length % 2 === 1 ? '0' + hex : hex;

// finally, turn it into an integer
return Fr.fromBuffer(Buffer.from(padded, 'hex'));
}

0 comments on commit 76d4177

Please sign in to comment.