Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
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
40 changes: 23 additions & 17 deletions boxes/boxes/vanilla/app/embedded-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
EmbeddedWallet as EmbeddedWalletBase,
type EmbeddedWalletOptions,
} from '@aztec/wallets/embedded';
import { NO_FROM, NoFrom } from '@aztec/aztec.js/account';

const logger = createLogger('wallet');
const LocalStorageKey = 'aztec-account';
Expand All @@ -43,7 +44,7 @@ export class EmbeddedWallet extends EmbeddedWalletBase {
* @returns - Complete fee options that can be used to create a transaction execution request
*/
override async completeFeeOptions(
from: AztecAddress,
from: AztecAddress | NoFrom,
feePayer?: AztecAddress,
gasSettings?: Partial<FieldsOf<GasSettings>>
): Promise<FeeOptions> {
Expand All @@ -52,21 +53,26 @@ export class EmbeddedWallet extends EmbeddedWalletBase {
(await this.aztecNode.getCurrentMinFees()).mul(1 + this.minFeePadding);
let accountFeePaymentMethodOptions;
let walletFeePaymentMethod;
// The transaction does not include a fee payment method, so we
// use the sponsoredFPC
if (!feePayer) {
accountFeePaymentMethodOptions = AccountFeePaymentMethodOptions.EXTERNAL;
const sponsoredFPCAddress = await this.#getSponsoredFPCAddress();

walletFeePaymentMethod = new SponsoredFeePaymentMethod(
sponsoredFPCAddress
);
} else {
// The transaction includes fee payment method, so we check if we are the fee payer for it
// (this can only happen if the embedded payment method is FeeJuiceWithClaim)
accountFeePaymentMethodOptions = from.equals(feePayer)
? AccountFeePaymentMethodOptions.FEE_JUICE_WITH_CLAIM
: AccountFeePaymentMethodOptions.EXTERNAL;
// If from is an address, we need to determine the appropriate fee payment method options for the
// account contract entrypoint to use
if (from !== NO_FROM) {
// The transaction does not include a fee payment method, so we
// use the sponsoredFPC
if (!feePayer) {
accountFeePaymentMethodOptions =
AccountFeePaymentMethodOptions.EXTERNAL;
const sponsoredFPCAddress = await this.#getSponsoredFPCAddress();

walletFeePaymentMethod = new SponsoredFeePaymentMethod(
sponsoredFPCAddress
);
} else {
// The transaction includes fee payment method, so we check if we are the fee payer for it
// (this can only happen if the embedded payment method is FeeJuiceWithClaim)
accountFeePaymentMethodOptions = from.equals(feePayer)
? AccountFeePaymentMethodOptions.FEE_JUICE_WITH_CLAIM
: AccountFeePaymentMethodOptions.EXTERNAL;
}
}
const fullGasSettings: GasSettings = GasSettings.default({
...gasSettings,
Expand Down Expand Up @@ -153,7 +159,7 @@ export class EmbeddedWallet extends EmbeddedWalletBase {
const sponsoredFPCAddress = await this.#getSponsoredFPCAddress();

const deployOpts: DeployAccountOptions<InteractionWaitOptions> = {
from: AztecAddress.ZERO,
from: NO_FROM,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NO_FROM sounds so strange, isn't there some other way to name this hinting at its effects? maybe something like PAYLOAD_DEFINED_FROM (which is probably wronger and horribler, but I'm trying to convey my objection)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seeing the changes to the docs, maybe CREATED_BY_TX

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in love with the naming too, I went for parallelism with NO_WAIT...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is weird, yes, but I'm also having trouble coming up with something better. Perhaps from: DIRECT_CALL? idk

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that NO_FROM sounds strange. Now that Claude is back up, it suggested a few options:

  • DIRECT or DIRECT_EXECUTION -- signals that the tx bypasses the account contract entrypoint
  • NO_ACCOUNT -- clearer that you're opting out of account contract wrapping
  • RAW -- short, implies no wrapping/mediation
  • SELF -- the transaction acts on its own behalf without an intermediary account contract
  • UNMEDIATED -- explicitly says "no account contract mediation"
  • ENTRYPOINTLESS -- describes the mechanism being skipped (the account entrypoint)
  • NO_SENDER -- closer to the domain: there's no "sender" account wrapping the call

And it even provided a ranking 😅

My ranking would be: DIRECT > NO_ACCOUNT > NO_SENDER > SELF > UNMEDIATED > ENTRYPOINTLESS > RAW > NO_FROM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry but all of those are worse IMO xD

from is pretty generic, it has no concept of account (wallet just happens to make it so from is an account, but we had the exception of the multicall entrypoint...which is not one). The NO_WAIT parallel is also a pro in my book

fee: {
paymentMethod: new SponsoredFeePaymentMethod(sponsoredFPCAddress),
},
Expand Down
8 changes: 6 additions & 2 deletions boxes/boxes/vanilla/scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import fs from 'fs';
import path from 'path';
// @ts-ignore
import { PrivateVotingContract } from '../artifacts/PrivateVoting.ts';
import { NO_FROM } from '@aztec/aztec.js/account';

const AZTEC_NODE_URL = process.env.AZTEC_NODE_URL || 'http://localhost:8080';
const WRITE_ENV_FILE = process.env.WRITE_ENV_FILE === 'false' ? false : true;
Expand Down Expand Up @@ -51,7 +52,7 @@ async function createAccount(wallet: EmbeddedWallet) {
const deployMethod = await accountManager.getDeployMethod();
const sponsoredPFCContract = await getSponsoredPFCContract();
const deployOpts: DeployAccountOptions<InteractionWaitOptions> = {
from: AztecAddress.ZERO,
from: NO_FROM,
fee: {
paymentMethod: new SponsoredFeePaymentMethod(
sponsoredPFCContract.address
Expand All @@ -71,7 +72,10 @@ async function deployContract(wallet: Wallet, deployer: AztecAddress) {

const sponsoredPFCContract = await getSponsoredPFCContract();

const { contract } = await PrivateVotingContract.deploy(wallet, deployer).send({
const { contract } = await PrivateVotingContract.deploy(
wallet,
deployer
).send({
from: deployer,
contractAddressSalt: salt,
fee: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ If your account already has Fee Juice (for example, [bridged from L1](./how_to_p

#include_code deploy_account_fee_juice /docs/examples/ts/aztecjs_connection/index.ts typescript

The `from: AztecAddress.ZERO` is required because there's no existing account to send from—the transaction itself creates the account.
The `from: NO_FROM` signals that this transaction should be executed without account contract mediation. The wallet will directly execute it via a default entrypoint with no authorization

## Verify deployment

Expand Down
56 changes: 56 additions & 0 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,62 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

### [Aztec.js] Use `NO_FROM` instead of `AztecAddress.ZERO` to bypass account contract entrypoint

When sending transactions that should not be mediated by an account contract (e.g., account contract self-deployments), use the explicit `NO_FROM` sentinel instead of `AztecAddress.ZERO`.

`NO_FROM` signals that the transaction should be executed directly via the `DefaultEntrypoint`. This replaces the brittle convention of passing `AztecAddress.ZERO` as the `from` field.

**Migration:**

```diff
- import { AztecAddress } from '@aztec/aztec.js';
+ import { NO_FROM } from '@aztec/aztec.js/account';

await contract.methods.my_method().send({
- from: AztecAddress.ZERO,
+ from: NO_FROM,
});
```

Note that `DefaultEntrypoint` only accepts a single call. If you need to execute multiple calls without account contract mediation (e.g., deploying an account contract and paying a fee in the same transaction), wrap them through `DefaultMultiCallEntrypoint` on the app side before sending:

```typescript
import { NO_FROM } from "@aztec/aztec.js/account";
import { DefaultMultiCallEntrypoint } from "@aztec/entrypoints/multicall";
import { mergeExecutionPayloads } from "@aztec/stdlib/tx";

// Merge multiple execution payloads into one
const merged = mergeExecutionPayloads([deployPayload, feePayload]);

// Wrap through multicall so it becomes a single call for DefaultEntrypoint
const multicall = new DefaultMultiCallEntrypoint();
const chainInfo = await wallet.getChainInfo();
const wrappedPayload = await multicall.wrapExecutionPayload(merged, chainInfo);

// Send without account contract mediation
await wallet.sendTx(wrappedPayload, { from: NO_FROM });
```

Using other contracts for wrapping (for example, supporting more calls) is also supported, as long as the contract is registered in the wallet. This opens the door to different flows that do not use account entrypoints as the first call in the chain, including app sponsored FPCs.

**Impact**: Any code that passes `AztecAddress.ZERO` as the `from` option in `.send()`, `.simulate()`, or deploy options must switch to `NO_FROM`. Wallets use `DefaultEntrypoint` directly for `NO_FROM` transactions, instead of the `DefaultMultiCallEntrypoint` that was used internally before when specifying `AztecAddress.ZERO`.

### [Aztec.js] `ExecuteUtilityOptions.scope` renamed to `scopes` and type changed to `AztecAddress[]`

The `scope` field in `ExecuteUtilityOptions` has been renamed to `scopes` and changed from a single `AztecAddress` to `AztecAddress[]`. This aligns the wallet's `executeUtility` API with the PXE API and `sendTx` in `Wallet`, which both accept an array of scopes.

**Migration:**

```diff
wallet.executeUtility(call, {
- scope: myAddress,
+ scopes: [myAddress],
});
```

**Impact**: Any code that calls `wallet.executeUtility` directly must update the options object. Wallets must update to adapt to the new interface

### [Aztec.nr] `attempt_note_discovery` now takes two separate functions instead of one

The `attempt_note_discovery` function (and related discovery functions like `do_sync_state`, `process_message_ciphertext`) now takes separate `compute_note_hash` and `compute_note_nullifier` arguments instead of a single combined `compute_note_hash_and_nullifier`. The corresponding type aliases are now `ComputeNoteHash` and `ComputeNoteNullifier` (instead of `ComputeNoteHashAndNullifier`).
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/ts/aztecjs_connection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ console.log("New account address:", newAccount.address.toString());

// docs:start:deploy_account_sponsored_fpc
// Additional imports needed for account deployment examples
import { AztecAddress } from "@aztec/aztec.js/addresses";
import { NO_FROM } from "@aztec/aztec.js/account";
import { SponsoredFeePaymentMethod } from "@aztec/aztec.js/fee/testing";
import { SponsoredFPCContract } from "@aztec/noir-contracts.js/SponsoredFPC";
import { getContractInstanceFromInstantiationParams } from "@aztec/stdlib/contract";
Expand All @@ -76,7 +76,7 @@ const sponsoredPaymentMethod = new SponsoredFeePaymentMethod(
// newAccount is the account created in the previous section
const deployMethod = await newAccount.getDeployMethod();
await deployMethod.send({
from: AztecAddress.ZERO,
from: NO_FROM,
fee: { paymentMethod: sponsoredPaymentMethod },
});
// docs:end:deploy_account_sponsored_fpc
Expand Down Expand Up @@ -144,7 +144,7 @@ console.log(`Alice's token balance: ${balance}`);
// Deploy the account using the bridged Fee Juice
const deployMethodFeeJuice = await feeJuiceAccount.getDeployMethod();
await deployMethodFeeJuice.send({
from: AztecAddress.ZERO,
from: NO_FROM,
fee: {
paymentMethod: new FeeJuicePaymentMethodWithClaim(
feeJuiceAccount.address,
Expand Down
24 changes: 15 additions & 9 deletions docs/examples/ts/recursive_verification/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import { getSponsoredFPCInstance } from "./scripts/sponsored_fpc.js";
import { SponsoredFPCContract } from "@aztec/noir-contracts.js/SponsoredFPC";
import { ValueNotEqualContract } from "./artifacts/ValueNotEqual.js";
import { EmbeddedWallet } from "@aztec/wallets/embedded";
import { AztecAddress } from "@aztec/aztec.js/addresses";
import { NO_FROM } from "@aztec/aztec.js/account";
import { Fr } from "@aztec/aztec.js/fields";
import assert from "node:assert";
import fs from "node:fs";
// docs:end:imports

// docs:start:sample_data
if (!fs.existsSync("data.json")) {
console.error("data.json not found. Run 'yarn data' first to generate proof data.");
console.error(
"data.json not found. Run 'yarn data' first to generate proof data.",
);
Comment on lines +17 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is your formatter configured to fewer than 120 chars?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the doc examples are...weird. I cannot get the ts server to behave with them and editing them does whatever. They also take forever to build...we have to talk to devrel about this

process.exit(1);
}
const data = JSON.parse(fs.readFileSync("data.json", "utf-8"));
Expand Down Expand Up @@ -57,7 +59,7 @@ async function main() {
// Deploy the account contract
const deployMethod = await manager.getDeployMethod();
await deployMethod.send({
from: AztecAddress.ZERO,
from: NO_FROM,
fee: { paymentMethod: sponsoredPaymentMethod },
});

Expand All @@ -84,9 +86,11 @@ async function main() {

// Step 3: Read initial counter value
// simulate() executes without submitting a transaction
let counterValue = (await valueNotEqual.methods
.get_counter(accounts[0].item)
.simulate({ from: accounts[0].item })).result;
let counterValue = (
await valueNotEqual.methods
.get_counter(accounts[0].item)
.simulate({ from: accounts[0].item })
).result;
console.log(`Counter value: ${counterValue}`); // Should be 10

// Step 4: Call increment() with proof data
Expand All @@ -107,9 +111,11 @@ async function main() {
await interaction.send(opts);

// Step 6: Read updated counter
counterValue = (await valueNotEqual.methods
.get_counter(accounts[0].item)
.simulate({ from: accounts[0].item })).result;
counterValue = (
await valueNotEqual.methods
.get_counter(accounts[0].item)
.simulate({ from: accounts[0].item })
).result;
console.log(`Counter value: ${counterValue}`); // Should be 11

assert(counterValue === 11n, "Counter should be 11 after verification");
Expand Down
5 changes: 4 additions & 1 deletion noir-projects/aztec-nr/aztec/src/authwit/auth.nr
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ struct CallAuthorization {
struct CallAuthorizationRequest {
selector: AuthorizationSelector,
inner_hash: Field,
on_behalf_of: AztecAddress,
msg_sender: AztecAddress,
fn_selector: FunctionSelector,
args_hash: Field,
Expand All @@ -221,11 +222,13 @@ struct CallAuthorizationRequest {
unconstrained fn emit_authorization_as_offchain_effect<let N: u32>(
authorization: CallAuthorization,
inner_hash: Field,
on_behalf_of: AztecAddress,
) {
let args: [Field; N] = load(authorization.args_hash);
let authorization_request = CallAuthorizationRequest {
selector: authorization.get_authorization_selector(),
inner_hash: inner_hash,
on_behalf_of: on_behalf_of,
msg_sender: authorization.msg_sender,
fn_selector: authorization.selector,
args_hash: authorization.args_hash,
Expand Down Expand Up @@ -253,7 +256,7 @@ pub fn assert_current_call_valid_authwit<let N: u32>(context: &mut PrivateContex
let inner_hash = compute_inner_authwit_hash(authorization.serialize());
// Safety: Offchain effects are by definition unconstrained. They are emitted via an oracle which we don't use for
// anything besides its side effects, therefore this is safe to call.
unsafe { emit_authorization_as_offchain_effect::<N>(authorization, inner_hash) };
unsafe { emit_authorization_as_offchain_effect::<N>(authorization, inner_hash, on_behalf_of) };

assert_inner_hash_valid_authwit(context, on_behalf_of, inner_hash);
}
Expand Down
3 changes: 2 additions & 1 deletion playground/src/components/home/components/Landing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { trackButtonClick } from '../../../utils/matomo';
import type { EmbeddedWallet } from '@aztec/wallets/embedded';
import { prepareForFeePayment } from '../../../utils/sponsoredFPC';
import { colors, commonStyles } from '../../../global.styles';
import { NO_FROM } from '@aztec/aztec.js/account';

const container = css({
display: 'flex',
Expand Down Expand Up @@ -399,7 +400,7 @@ export function Landing() {

const deployMethod = await accountManager.getDeployMethod();
const opts: DeployAccountOptions = {
from: AztecAddress.ZERO,
from: NO_FROM,
fee: {
paymentMethod,
},
Expand Down
3 changes: 2 additions & 1 deletion playground/src/wallet/components/CreateAccountDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { INFO_TEXT } from '../../constants';
import { Box, DialogContent } from '@mui/material';
import { DialogActions } from '@mui/material';
import type { EmbeddedWallet } from '@aztec/wallets/embedded';
import { NO_FROM } from '@aztec/aztec.js/account';

export function CreateAccountDialog({
wallet,
Expand Down Expand Up @@ -77,7 +78,7 @@ export function CreateAccountDialog({
if (publiclyDeploy) {
deployMethod = await accountManager.getDeployMethod();
opts = {
from: AztecAddress.ZERO,
from: NO_FROM,
fee: {
paymentMethod: feePaymentMethod,
},
Expand Down
46 changes: 0 additions & 46 deletions yarn-project/aztec.js/src/account/signerless_account.ts

This file was deleted.

2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/api/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ export {
export type { AuthWitnessProvider, ChainInfo } from '@aztec/entrypoints/interfaces';
export { ChainInfoSchema } from '@aztec/entrypoints/interfaces';

export { SignerlessAccount } from '../account/signerless_account.js';
export { NO_FROM, type NoFrom } from '../contract/interaction_options.js';
Loading
Loading