Skip to content
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Fr } from '@aztec/foundation/curves/bn254';
import { FieldReader } from '@aztec/foundation/serialize';
import { AuthorizationSelector, FunctionSelector } from '@aztec/stdlib/abi';
import { computeInnerAuthWitHash } from '@aztec/stdlib/auth-witness';
import { AztecAddress } from '@aztec/stdlib/aztec-address';
import { computeVarArgsHash } from '@aztec/stdlib/hash';

/**
* An authwit request for a function call. Includes the preimage of the data
Expand Down Expand Up @@ -38,6 +40,26 @@ export class CallAuthorizationRequest {
public args: Fr[],
) {}

/** Validates that innerHash and argsHash are consistent with the provided preimage fields. */
async validate(): Promise<void> {
Comment thread
Thunkar marked this conversation as resolved.
Outdated
const expectedArgsHash = await computeVarArgsHash(this.args);
if (!expectedArgsHash.equals(this.argsHash)) {
throw new Error(
`CallAuthorizationRequest argsHash mismatch: expected ${expectedArgsHash.toString()}, got ${this.argsHash.toString()}`,
);
}
const expectedInnerHash = await computeInnerAuthWitHash([
this.msgSender.toField(),
this.functionSelector.toField(),
this.argsHash,
]);
if (!expectedInnerHash.equals(this.innerHash)) {
throw new Error(
`CallAuthorizationRequest innerHash mismatch: expected ${expectedInnerHash.toString()}, got ${this.innerHash.toString()}`,
);
}
}

static getSelector(): Promise<AuthorizationSelector> {
return AuthorizationSelector.fromSignature('CallAuthorization((Field),(u32),Field)');
}
Expand All @@ -51,13 +73,15 @@ export class CallAuthorizationRequest {
`Invalid authorization selector for CallAuthwit: expected ${expectedSelector.toString()}, got ${selector.toString()}`,
);
}
return new CallAuthorizationRequest(
const request = new CallAuthorizationRequest(
selector,
reader.readField(),
AztecAddress.fromField(reader.readField()),
FunctionSelector.fromField(reader.readField()),
reader.readField(),
reader.readFieldArray(reader.remainingFields()),
);
await request.validate();
return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ export async function generateSimulatedProvingResult(
privateExecutionResult.entrypoint.publicInputs.anchorBlockHeader.globalVariables.timestamp +
BigInt(MAX_TX_LIFETIME);

let feePayer = AztecAddress.zero();

const executions = [privateExecutionResult.entrypoint];

while (executions.length !== 0) {
Expand All @@ -462,6 +464,13 @@ export async function generateSimulatedProvingResult(

const { contractAddress } = execution.publicInputs.callContext;

if (execution.publicInputs.isFeePayer) {
if (!feePayer.isZero()) {
throw new Error('Multiple fee payers found in private execution result');
}
feePayer = contractAddress;
}
Comment thread
Thunkar marked this conversation as resolved.

scopedNoteHashes.push(
...execution.publicInputs.noteHashes
.getActiveItems()
Expand Down Expand Up @@ -682,7 +691,7 @@ export async function generateSimulatedProvingResult(
daGas: TX_DA_GAS_OVERHEAD,
}),
),
/*feePayer=*/ AztecAddress.zero(),
/*feePayer=*/ feePayer,
/*expirationTimestamp=*/ expirationTimestamp,
hasPublicCalls ? inputsForPublic : undefined,
!hasPublicCalls ? inputsForRollup : undefined,
Expand Down
68 changes: 67 additions & 1 deletion yarn-project/wallets/src/embedded/embedded_wallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { type Account, SignerlessAccount } from '@aztec/aztec.js/account';
import type { Aliased } from '@aztec/aztec.js/wallet';
import { CallAuthorizationRequest } from '@aztec/aztec.js/authorization';
import { type InteractionWaitOptions, type SendReturn, getGasLimits } from '@aztec/aztec.js/contracts';
import type { Aliased, SendOptions } from '@aztec/aztec.js/wallet';
import { AccountManager } from '@aztec/aztec.js/wallet';
import type { DefaultAccountEntrypointOptions } from '@aztec/entrypoints/account';
import { Fq, Fr } from '@aztec/foundation/curves/bn254';
Expand All @@ -8,12 +10,14 @@ import type { AccessScopes, PXEConfig, PXECreationOptions } from '@aztec/pxe/cli
import type { PXE } from '@aztec/pxe/server';
import { AztecAddress } from '@aztec/stdlib/aztec-address';
import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
import { GasSettings } from '@aztec/stdlib/gas';
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
import { deriveSigningKey } from '@aztec/stdlib/keys';
import {
ExecutionPayload,
SimulationOverrides,
type TxSimulationResult,
collectOffchainEffects,
mergeExecutionPayloads,
} from '@aztec/stdlib/tx';
import { BaseWallet, type FeeOptions } from '@aztec/wallet-sdk/base-wallet';
Expand All @@ -33,6 +37,8 @@ export type EmbeddedWalletOptions = {
};

export class EmbeddedWallet extends BaseWallet {
protected estimatedGasPadding = 0.2;

constructor(
pxe: PXE,
aztecNode: AztecNode,
Expand Down Expand Up @@ -79,6 +85,62 @@ export class EmbeddedWallet extends BaseWallet {
return storedSenders;
}

public override async sendTx<W extends InteractionWaitOptions = undefined>(
Comment thread
Thunkar marked this conversation as resolved.
executionPayload: ExecutionPayload,
opts: SendOptions<W>,
): Promise<SendReturn<W>> {
const feeOptions = await this.completeFeeOptionsForEstimation(
opts.from,
executionPayload.feePayer,
opts.fee?.gasSettings,
);

// Simulate the transaction first to estimate gas and capture required
// private authwitesses based on offchain effects.
const simulationResult = await this.simulateViaEntrypoint(
executionPayload,
opts.from,
feeOptions,
this.scopesFrom(opts.from, opts.additionalScopes),
true,
Comment thread
Thunkar marked this conversation as resolved.
Outdated
);

const offchainEffects = collectOffchainEffects(simulationResult.privateExecutionResult);
const authWitnesses = await Promise.all(
offchainEffects.map(async effect => {
try {
const authRequest = await CallAuthorizationRequest.fromFields(effect.data);
return this.createAuthWit(opts.from, {
consumer: effect.contractAddress,
innerHash: authRequest.innerHash,
});
} catch {
return undefined;
Comment thread
Thunkar marked this conversation as resolved.
}
}),
);
for (const authwit of authWitnesses) {
if (authwit) {
executionPayload.authWitnesses.push(authwit);
}
}
const estimated = getGasLimits(simulationResult, this.estimatedGasPadding);
this.log.verbose(
`Estimated gas limits for tx: DA=${estimated.gasLimits.daGas} L2=${estimated.gasLimits.l2Gas} teardownDA=${estimated.teardownGasLimits.daGas} teardownL2=${estimated.teardownGasLimits.l2Gas}`,
);
const gasSettings = GasSettings.from({
...opts.fee?.gasSettings,
maxFeesPerGas: feeOptions.gasSettings.maxFeesPerGas,
maxPriorityFeesPerGas: feeOptions.gasSettings.maxPriorityFeesPerGas,
gasLimits: opts.fee?.gasSettings?.gasLimits ?? estimated.gasLimits,
teardownGasLimits: opts.fee?.gasSettings?.teardownGasLimits ?? estimated.teardownGasLimits,
});
return super.sendTx(executionPayload, {
...opts,
fee: { ...opts.fee, gasSettings },
});
}

/**
* Simulates calls via a stub account entrypoint, bypassing real account authorization.
* This allows kernelless simulation with contract overrides, skipping expensive
Expand Down Expand Up @@ -220,6 +282,10 @@ export class EmbeddedWallet extends BaseWallet {
this.minFeePadding = value ?? 0.5;
}

setEstimatedGasPadding(value?: number) {
this.estimatedGasPadding = value ?? 0.2;
Comment thread
Thunkar marked this conversation as resolved.
Outdated
}

stop() {
return this.pxe.stop();
}
Expand Down
Loading