Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boxes/boxes/vanilla/app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ async function updateVoteTally(wallet: Wallet, from: AztecAddress) {
)
);

const batchResult = await new BatchCall(wallet, payloads).simulate({ from });
const { result: batchResult } = await new BatchCall(wallet, payloads).simulate({ from });

batchResult.forEach(({ result: value }, i) => {
results[i + 1] = value;
Expand Down
12 changes: 6 additions & 6 deletions yarn-project/aztec.js/src/contract/batch_call.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe('BatchCall', () => {
{ name: 'simulateTx', result: txSimResult },
] as any);

const results = await batchCall.simulate({ from: await AztecAddress.random() });
const { result: results } = await batchCall.simulate({ from: await AztecAddress.random() });

// Verify wallet.batch was called once with both utility calls AND simulateTx
expect(wallet.batch).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -212,7 +212,7 @@ describe('BatchCall', () => {
{ name: 'executeUtility', result: utilityResult2 },
] as any);

const results = await batchCall.simulate({ from: await AztecAddress.random() });
const { result: results } = await batchCall.simulate({ from: await AztecAddress.random() });

expect(wallet.batch).toHaveBeenCalledTimes(1);
expect(wallet.batch).toHaveBeenCalledWith([
Expand Down Expand Up @@ -247,7 +247,7 @@ describe('BatchCall', () => {
const utilityResult = UtilityExecutionResult.random();
wallet.batch.mockResolvedValue([{ name: 'executeUtility', result: utilityResult }] as any);

const results = await batchCall.simulate({ from: await AztecAddress.random() });
const { result: results } = await batchCall.simulate({ from: await AztecAddress.random() });

expect(results).toHaveLength(1);
expect(results[0].offchainEffects).toEqual([]);
Expand Down Expand Up @@ -307,7 +307,7 @@ describe('BatchCall', () => {
{ name: 'simulateTx', result: txSimResult },
] as any);

const results = await batchCall.simulate({ from: await AztecAddress.random() });
const { result: results } = await batchCall.simulate({ from: await AztecAddress.random() });
expect(results).toHaveLength(3);

expect(results[0].offchainMessages).toEqual([
Expand Down Expand Up @@ -349,7 +349,7 @@ describe('BatchCall', () => {

wallet.batch.mockResolvedValue([{ name: 'simulateTx', result: txSimResult }] as any);

const results = await batchCall.simulate({ from: await AztecAddress.random() });
const { result: results } = await batchCall.simulate({ from: await AztecAddress.random() });

expect(wallet.batch).toHaveBeenCalledTimes(1);
expect(wallet.batch).toHaveBeenCalledWith([
Expand All @@ -376,7 +376,7 @@ describe('BatchCall', () => {
it('should handle empty batch', async () => {
batchCall = new BatchCall(wallet, []);

const results = await batchCall.simulate({ from: await AztecAddress.random() });
const { result: results } = await batchCall.simulate({ from: await AztecAddress.random() });

expect(wallet.batch).not.toHaveBeenCalled();
expect(results).toEqual([]);
Expand Down
30 changes: 23 additions & 7 deletions yarn-project/aztec.js/src/contract/batch_call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { ExecutionPayload, TxSimulationResult, UtilityExecutionResult, mergeExec

import type { BatchedMethod, Wallet } from '../wallet/wallet.js';
import { BaseContractInteraction } from './base_contract_interaction.js';
import { getGasLimits } from './get_gas_limits.js';
import {
type RequestInteractionOptions,
type SimulateInteractionOptions,
type SimulationResult,
extractOffchainOutput,
toSimulateOptions,
} from './interaction_options.js';
Expand Down Expand Up @@ -45,7 +47,7 @@ export class BatchCall extends BaseContractInteraction {
* @param options - An optional object containing additional configuration for the interaction.
* @returns The results of all the interactions that make up the batch
*/
public async simulate(options: SimulateInteractionOptions): Promise<any> {
public async simulate(options: SimulateInteractionOptions): Promise<SimulationResult> {
const { indexedExecutionPayloads, utility } = (await this.getExecutionPayloads()).reduce<{
/** Keep track of the number of private calls to retrieve the return values */
privateIndex: 0;
Expand Down Expand Up @@ -119,32 +121,46 @@ export class BatchCall extends BaseContractInteraction {
}

// Process tx simulation result (it comes last if present)
let simulatedTx: TxSimulationResult | undefined;
if (indexedExecutionPayloads.length > 0) {
const txResultWrapper = batchResults[utility.length];
if (txResultWrapper.name === 'simulateTx') {
const simulatedTx = txResultWrapper.result as TxSimulationResult;
simulatedTx = txResultWrapper.result as TxSimulationResult;
indexedExecutionPayloads.forEach(([request, callIndex, resultIndex]) => {
const call = request.calls[0];
// As account entrypoints are private, for private functions we retrieve the return values from the first nested call
// since we're interested in the first set of values AFTER the account entrypoint
// For public functions we retrieve the first values directly from the public output.
const rawReturnValues =
call.type == FunctionType.PRIVATE
? simulatedTx.getPrivateReturnValues()?.nested?.[resultIndex].values
: simulatedTx.getPublicReturnValues()?.[resultIndex].values;
? simulatedTx!.getPrivateReturnValues()?.nested?.[resultIndex].values
: simulatedTx!.getPublicReturnValues()?.[resultIndex].values;

results[callIndex] = {
result: rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : [],
...extractOffchainOutput(
simulatedTx.offchainEffects,
simulatedTx.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp,
simulatedTx!.offchainEffects,
simulatedTx!.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp,
),
};
});
}
}

return results;
if ((options.includeMetadata || options.fee?.estimateGas) && simulatedTx) {
const { gasLimits, teardownGasLimits } = getGasLimits(simulatedTx, options.fee?.estimatedGasPadding);
this.log.verbose(
`Estimated gas limits for batch tx: DA=${gasLimits.daGas} L2=${gasLimits.l2Gas} teardownDA=${teardownGasLimits.daGas} teardownL2=${teardownGasLimits.l2Gas}`,
);
return {
result: results,
estimatedGas: { gasLimits, teardownGasLimits },
offchainEffects: [],
offchainMessages: [],
};
}

return { result: results, offchainEffects: [], offchainMessages: [] };
}

protected async getExecutionPayloads(): Promise<ExecutionPayload[]> {
Expand Down
89 changes: 65 additions & 24 deletions yarn-project/bot/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
import { TokenContract } from '@aztec/noir-contracts.js/Token';
import { TestContract } from '@aztec/noir-test-contracts.js/Test';
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
import { GasSettings } from '@aztec/stdlib/gas';
import { GasFees, GasSettings } from '@aztec/stdlib/gas';
import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
import { deriveSigningKey } from '@aztec/stdlib/keys';
import { EmbeddedWallet } from '@aztec/wallets/embedded';
Expand Down Expand Up @@ -223,15 +223,20 @@ export class BotFactory {
const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
const deployMethod = await accountManager.getDeployMethod();
const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
const gasSettings = GasSettings.default({ maxFeesPerGas });

const { estimatedGas } = await deployMethod.simulate({
from: AztecAddress.ZERO,
fee: { estimateGas: true, paymentMethod },
});
const gasSettings = GasSettings.from({ ...estimatedGas!, maxFeesPerGas, maxPriorityFeesPerGas: GasFees.empty() });

await this.withNoMinTxsPerBlock(async () => {
const { txHash } = await deployMethod.send({
from: AztecAddress.ZERO,
fee: { gasSettings, paymentMethod },
wait: NO_WAIT,
});
this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`, { gasSettings });
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
});
this.log.info(`Account deployed at ${address}`);
Expand Down Expand Up @@ -297,8 +302,9 @@ export class BotFactory {
await deploy.register();
} else {
this.log.info(`Deploying token contract at ${address.toString()}`);
const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT });
this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`);
const { estimatedGas } = await deploy.simulate({ ...deployOpts, fee: { estimateGas: true } });
const { txHash } = await deploy.send({ ...deployOpts, fee: { gasSettings: estimatedGas }, wait: NO_WAIT });
this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`, { estimatedGas });
await this.withNoMinTxsPerBlock(async () => {
await waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
return token;
Expand Down Expand Up @@ -338,10 +344,19 @@ export class BotFactory {
const amm = AMMContract.at(instance.address, this.wallet);

this.log.info(`AMM deployed at ${amm.address}`);
const { receipt: minterReceipt } = await lpToken.methods
.set_minter(amm.address, true)
.send({ from: deployer, wait: { timeout: this.config.txMinedWaitSeconds } });
this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`);
const setMinterInteraction = lpToken.methods.set_minter(amm.address, true);
const { estimatedGas: setMinterGas } = await setMinterInteraction.simulate({
from: deployer,
fee: { estimateGas: true },
});
const { receipt: minterReceipt } = await setMinterInteraction.send({
from: deployer,
fee: { gasSettings: setMinterGas },
wait: { timeout: this.config.txMinedWaitSeconds },
});
this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`, {
estimatedGas: setMinterGas,
});
this.log.info(`Liquidity token initialized`);

return amm;
Expand Down Expand Up @@ -409,22 +424,44 @@ export class BotFactory {
.getFunctionCall(),
});

const { receipt: mintReceipt } = await new BatchCall(this.wallet, [
const mintBatch = new BatchCall(this.wallet, [
token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
]).send({ from: liquidityProvider, wait: { timeout: this.config.txMinedWaitSeconds } });
]);
const { estimatedGas: mintGas } = await mintBatch.simulate({
from: liquidityProvider,
fee: { estimateGas: true },
});
const { receipt: mintReceipt } = await mintBatch.send({
from: liquidityProvider,
fee: { gasSettings: mintGas },
wait: { timeout: this.config.txMinedWaitSeconds },
});

this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`, { estimatedGas: mintGas });

const { receipt: addLiquidityReceipt } = await amm.methods
.add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce)
.send({
from: liquidityProvider,
authWitnesses: [token0Authwit, token1Authwit],
wait: { timeout: this.config.txMinedWaitSeconds },
});
const addLiquidityInteraction = amm.methods.add_liquidity(
amount0Max,
amount1Max,
amount0Min,
amount1Min,
authwitNonce,
);
const { estimatedGas: addLiquidityGas } = await addLiquidityInteraction.simulate({
from: liquidityProvider,
fee: { estimateGas: true },
authWitnesses: [token0Authwit, token1Authwit],
});
const { receipt: addLiquidityReceipt } = await addLiquidityInteraction.send({
from: liquidityProvider,
fee: { gasSettings: addLiquidityGas },
authWitnesses: [token0Authwit, token1Authwit],
wait: { timeout: this.config.txMinedWaitSeconds },
});

this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`);
this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`, {
estimatedGas: addLiquidityGas,
});
this.log.info(`Liquidity added`);

const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
Expand All @@ -445,9 +482,10 @@ export class BotFactory {
this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
await deploy.register();
} else {
this.log.info(`Deploying contract ${name} at ${address.toString()}`);
const { estimatedGas } = await deploy.simulate({ ...deployOpts, fee: { estimateGas: true } });
this.log.info(`Deploying contract ${name} at ${address.toString()}`, { estimatedGas });
await this.withNoMinTxsPerBlock(async () => {
const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT });
const { txHash } = await deploy.send({ ...deployOpts, fee: { gasSettings: estimatedGas }, wait: NO_WAIT });
this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
});
Expand Down Expand Up @@ -491,13 +529,16 @@ export class BotFactory {

// PrivateToken's mint accesses contract-level private storage vars (admin, total_supply).
const additionalScopes = isStandardToken ? undefined : [token.address];
const mintBatch = new BatchCall(token.wallet, calls);
const { estimatedGas } = await mintBatch.simulate({ from: minter, fee: { estimateGas: true }, additionalScopes });
await this.withNoMinTxsPerBlock(async () => {
const { txHash } = await new BatchCall(token.wallet, calls).send({
const { txHash } = await mintBatch.send({
from: minter,
additionalScopes,
fee: { gasSettings: estimatedGas },
wait: NO_WAIT,
});
this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
this.log.info(`Sent token mint tx with hash ${txHash.toString()}`, { estimatedGas });
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
});
}
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_state_vars.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('e2e_state_vars', () => {
contract.methods.get_public_immutable_constrained_private_indirect(),
contract.methods.get_public_immutable(),
]).simulate({ from: defaultAccountAddress })
).map((r: any) => r.result);
).result.map((r: any) => r.result);

expect(a).toEqual(c);
expect(b).toEqual({ account: c.account, value: c.value + 1n });
Expand All @@ -87,7 +87,7 @@ describe('e2e_state_vars', () => {
contract.methods.get_public_immutable_constrained_public_indirect(),
contract.methods.get_public_immutable(),
]).simulate({ from: defaultAccountAddress })
).map((r: any) => r.result);
).result.map((r: any) => r.result);

expect(a).toEqual(c);
expect(b).toEqual({ account: c.account, value: c.value + 1n });
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/simulators/token_simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export class TokenSimulator {
chunk(calls, 5).map(batch => new BatchCall(this.defaultWallet, batch).simulate({ from: this.defaultAddress })),
)
)
.flat()
.flatMap(r => r.result)
.map(r => r.result);
expect(results[0]).toEqual(this.totalSupply);

Expand Down
Loading