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: 2 additions & 0 deletions noir-projects/aztec-nr/aztec/src/discovery/private_logs.nr
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ pub unconstrained fn do_process_log<Env>(
recipient: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
) {
debug_log_format("Processing log with tag {0}", [log.get(0)]);

let log_plaintext = decrypt_log(log, recipient);

// The first thing to do after decrypting the log is to determine what type of private log we're processing. We
Expand Down
7 changes: 7 additions & 0 deletions noir-projects/aztec-nr/uint-note/src/uint_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ impl PartialUintNote {
impl PartialUintNote {
/// Completes the partial note, creating a new note that can be used like any other UintNote.
pub fn complete(self, value: u128, context: &mut PublicContext) {
// A note with a value of zero is valid, but we cannot currently complete a partial note with such a value
// because this will result in the completion log having its last field set to 0. Public logs currently do not
// track their length, and so trailing zeros are simply trimmed. This results in the completion log missing its
// last field (the value), and note discovery failing.
// TODO(#11636): remove this
assert(value != 0, "Cannot complete a PartialUintNote with a value of 0");

// We need to do two things:
// - emit a public log containing the public fields (the value). The contract will later find it by searching
// for the expected tag (which is simply the partial note commitment).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ impl PartialNFTNote {
impl PartialNFTNote {
/// Completes the partial note, creating a new note that can be used like any other NFTNote.
pub fn complete(self, token_id: Field, context: &mut PublicContext) {
// A note with a value of zero is valid, but we cannot currently complete a partial note with such a value
// because this will result in the completion log having its last field set to 0. Public logs currently do not
// track their length, and so trailing zeros are simply trimmed. This results in the completion log missing its
// last field (the value), and note discovery failing.
// TODO(#11636): remove this
assert(token_id != 0, "Cannot complete a PartialNFTNote with a value of 0");

// We need to do two things:
// - emit a public log containing the public fields (the token id). The contract will later find it by
// searching for the expected tag (which is simply the partial note commitment).
Expand Down
2 changes: 2 additions & 0 deletions spartan/aztec-network/templates/transaction-bot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ spec:
value: "{{ .Values.bot.botNoStart }}"
- name: BOT_FEE_PAYMENT_METHOD
value: "{{ .Values.bot.feePaymentMethod }}"
- name: BOT_AMM_TXS
value: "{{ .Values.bot.ammTxs }}"
- name: PXE_PROVER_ENABLED
value: "{{ .Values.aztec.realProofs }}"
- name: PROVER_REAL_PROOFS
Expand Down
1 change: 1 addition & 0 deletions spartan/aztec-network/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ bot:
followChain: "NONE"
botNoStart: false
feePaymentMethod: "fee_juice"
ammTxs: false
maxErrors: 3
stopIfUnhealthy: true
service:
Expand Down
8 changes: 4 additions & 4 deletions spartan/aztec-network/values/rc-1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ proverAgent:
cpu: "31"

bot:
# 2 bots each building txs as fast as possible
# It takes about 16 seconds to build a tx so each bot should produce ~2 txs per block
replicas: 2
followChain: "NONE"
# 4 bots each building txs as fast as possible
replicas: 4
ammTxs: true
followChain: "PENDING"
enabled: true
txIntervalSeconds: 1
resources:
Expand Down
26 changes: 19 additions & 7 deletions yarn-project/aztec.js/src/contract/batch_call.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ExecutionPayload } from '@aztec/entrypoints/payload';
import { ExecutionPayload } from '@aztec/entrypoints/payload';
import { mergeExecutionPayloads } from '@aztec/entrypoints/payload';
import { type FunctionCall, FunctionType, decodeFromAbi } from '@aztec/stdlib/abi';
import type { TxExecutionRequest } from '@aztec/stdlib/tx';
Expand Down Expand Up @@ -38,8 +38,14 @@ export class BatchCall extends BaseContractInteraction {
* @returns An execution payload wrapped in promise.
*/
public async request(options: RequestMethodOptions = {}): Promise<ExecutionPayload> {
const requests = await this.getRequests(options);
return mergeExecutionPayloads(requests);
const requests = await this.getRequests();
const combinedPayload = mergeExecutionPayloads(requests);
return new ExecutionPayload(
combinedPayload.calls,
combinedPayload.authWitnesses.concat(options.authWitnesses ?? []),
combinedPayload.capsules.concat(options.capsules ?? []),
combinedPayload.extraHashedArgs,
);
}

/**
Expand All @@ -52,7 +58,7 @@ export class BatchCall extends BaseContractInteraction {
* @returns The result of the transaction as returned by the contract function.
*/
public async simulate(options: SimulateMethodOptions = {}): Promise<any> {
const { indexedExecutionPayloads, unconstrained } = (await this.getRequests(options)).reduce<{
const { indexedExecutionPayloads, unconstrained } = (await this.getRequests()).reduce<{
/** Keep track of the number of private calls to retrieve the return values */
privateIndex: 0;
/** Keep track of the number of public calls to retrieve the return values */
Expand All @@ -79,7 +85,13 @@ export class BatchCall extends BaseContractInteraction {
);

const payloads = indexedExecutionPayloads.map(([request]) => request);
const requestWithoutFee = mergeExecutionPayloads(payloads);
const combinedPayload = mergeExecutionPayloads(payloads);
const requestWithoutFee = new ExecutionPayload(
combinedPayload.calls,
combinedPayload.authWitnesses.concat(options.authWitnesses ?? []),
combinedPayload.capsules.concat(options.capsules ?? []),
combinedPayload.extraHashedArgs,
);
const { fee: userFee, nonce, cancellable } = options;
const fee = await this.getFeeOptions(requestWithoutFee, userFee, {});
const txRequest = await this.wallet.createTxExecutionRequest(requestWithoutFee, fee, { nonce, cancellable });
Expand Down Expand Up @@ -117,7 +129,7 @@ export class BatchCall extends BaseContractInteraction {
return results;
}

private async getRequests(options: RequestMethodOptions = {}) {
return await Promise.all(this.calls.map(c => c.request(options)));
private async getRequests() {
return await Promise.all(this.calls.map(c => c.request()));
}
}
93 changes: 93 additions & 0 deletions yarn-project/bot/src/amm_bot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { AztecAddress, Fr, SentTx, type Wallet } from '@aztec/aztec.js';
import type { AMMContract } from '@aztec/noir-contracts.js/AMM';
import type { TokenContract } from '@aztec/noir-contracts.js/Token';
import type { AztecNode, AztecNodeAdmin, PXE } from '@aztec/stdlib/interfaces/client';

import { BaseBot } from './base_bot.js';
import type { BotConfig } from './config.js';
import { BotFactory } from './factory.js';

const TRANSFER_AMOUNT = 1_000;

type Balances = { token0: bigint; token1: bigint };

export class AmmBot extends BaseBot {
protected constructor(
pxe: PXE,
wallet: Wallet,
public readonly amm: AMMContract,
public readonly token0: TokenContract,
public readonly token1: TokenContract,
config: BotConfig,
) {
super(pxe, wallet, config);
}

static async create(
config: BotConfig,
dependencies: { pxe?: PXE; node?: AztecNode; nodeAdmin?: AztecNodeAdmin },
): Promise<AmmBot> {
const { pxe, wallet, token0, token1, amm } = await new BotFactory(config, dependencies).setupAmm();
return new AmmBot(pxe, wallet, amm, token0, token1, config);
}

protected async createAndSendTx(logCtx: object): Promise<SentTx> {
const { feePaymentMethod } = this.config;
const { wallet, amm, token0, token1 } = this;

this.log.verbose(`Preparing tx with ${feePaymentMethod} fee to swap tokens`, logCtx);

const ammBalances = await this.getAmmBalances();
const amountIn = TRANSFER_AMOUNT;
const nonce = Fr.random();

const swapAuthwit = await wallet.createAuthWit({
caller: amm.address,
action: token0.methods.transfer_to_public(wallet.getAddress(), amm.address, amountIn, nonce),
});

const amountOutMin = await amm.methods
.get_amount_out_for_exact_in(ammBalances.token0, ammBalances.token1, amountIn)
.simulate();

const swapExactTokensInteraction = amm.methods.swap_exact_tokens_for_tokens(
token0.address,
token1.address,
amountIn,
amountOutMin,
nonce,
);

const opts = this.getSendMethodOpts(swapAuthwit);

this.log.verbose(`Proving transaction`, logCtx);
const tx = await swapExactTokensInteraction.prove(opts);

return tx.send();
}

public getAmmBalances(): Promise<Balances> {
return this.getPublicBalanceFor(this.amm.address);
}

public async getBalances(): Promise<{ senderPublic: Balances; senderPrivate: Balances; amm: Balances }> {
return {
senderPublic: await this.getPublicBalanceFor(this.wallet.getAddress()),
senderPrivate: await this.getPrivateBalanceFor(this.wallet.getAddress()),
amm: await this.getPublicBalanceFor(this.amm.address),
};
}

private async getPublicBalanceFor(address: AztecAddress): Promise<Balances> {
return {
token0: await this.token0.methods.balance_of_public(address).simulate(),
token1: await this.token1.methods.balance_of_public(address).simulate(),
};
}
private async getPrivateBalanceFor(address: AztecAddress): Promise<Balances> {
return {
token0: await this.token0.methods.balance_of_private(address).simulate(),
token1: await this.token1.methods.balance_of_private(address).simulate(),
};
}
}
75 changes: 75 additions & 0 deletions yarn-project/bot/src/base_bot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
AuthWitness,
FeeJuicePaymentMethod,
type SendMethodOptions,
SentTx,
type Wallet,
createLogger,
waitForProven,
} from '@aztec/aztec.js';
import { Gas } from '@aztec/stdlib/gas';
import type { PXE } from '@aztec/stdlib/interfaces/client';

import type { BotConfig } from './config.js';

export abstract class BaseBot {
protected log = createLogger('bot');

protected attempts: number = 0;
protected successes: number = 0;

protected constructor(public readonly pxe: PXE, public readonly wallet: Wallet, public config: BotConfig) {}

public async run() {
this.attempts++;
const logCtx = { runId: Date.now() * 1000 + Math.floor(Math.random() * 1000) };
const { followChain, txMinedWaitSeconds } = this.config;

this.log.verbose(`Creating tx`, logCtx);
const tx = await this.createAndSendTx(logCtx);

const txHash = await tx.getTxHash();

if (followChain === 'NONE') {
this.log.info(`Transaction ${txHash} sent, not waiting for it to be mined`);
return;
}

this.log.verbose(
`Awaiting tx ${txHash} to be on the ${followChain} chain (timeout ${txMinedWaitSeconds}s)`,
logCtx,
);
const receipt = await tx.wait({
timeout: txMinedWaitSeconds,
});
if (followChain === 'PROVEN') {
await waitForProven(this.pxe, receipt, { provenTimeout: txMinedWaitSeconds });
}
this.successes++;
this.log.info(
`Tx #${this.attempts} ${receipt.txHash} successfully mined in block ${receipt.blockNumber} (stats: ${this.successes}/${this.attempts} success)`,
logCtx,
);
}

protected abstract createAndSendTx(logCtx: object): Promise<SentTx>;

protected getSendMethodOpts(...authWitnesses: AuthWitness[]): SendMethodOptions {
const sender = this.wallet.getAddress();
const { l2GasLimit, daGasLimit, skipPublicSimulation } = this.config;
const paymentMethod = new FeeJuicePaymentMethod(sender);

let gasSettings, estimateGas;
if (l2GasLimit !== undefined && l2GasLimit > 0 && daGasLimit !== undefined && daGasLimit > 0) {
gasSettings = { gasLimits: Gas.from({ l2Gas: l2GasLimit, daGas: daGasLimit }) };
estimateGas = false;
this.log.verbose(`Using gas limits ${l2GasLimit} L2 gas ${daGasLimit} DA gas`);
} else {
estimateGas = true;
this.log.verbose(`Estimating gas for transaction`);
}
const baseFeePadding = 2; // Send 3x the current base fee
this.log.verbose(skipPublicSimulation ? `Skipping public simulation` : `Simulating public transfers`);
return { fee: { estimateGas, paymentMethod, gasSettings, baseFeePadding }, skipPublicSimulation, authWitnesses };
}
}
Loading
Loading