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
11 changes: 11 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,17 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

### [Aztec.js] `AccountManager.create` takes an options bag

`AccountManager.create` no longer takes `salt` as a positional argument. The trailing `salt?: Salt` parameter has been folded into a new `AccountManagerCreateOptions` bag alongside `immutablesHash` and `deployer`:

```diff
- AccountManager.create(wallet, secret, accountContract, salt)
+ AccountManager.create(wallet, secret, accountContract, { salt })
```

`immutablesHash` lets callers commit a non-zero immutables hash on the resulting `ContractInstance` (folded into the salted initialization hash, so it affects the derived address). `deployer` overrides the deployer address recorded on the instance (defaults to `AztecAddress.ZERO`). The same `immutablesHash` field is now also threaded through `DeployMethod` / `DeployAccountMethod` so the address derived at deploy time matches the one on `accountManager.getInstance()`.

### [Aztec.nr] Defining a custom `sync_state` function now requires `AztecConfig`

Contracts that previously overrode the default `sync_state` by defining their own function with that name will now get a compile error. Use `AztecConfig::custom_sync_state()` instead.
Expand Down
4 changes: 1 addition & 3 deletions yarn-project/aztec.js/src/account/account_with_secret_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { computeAddressSecret, deriveMasterIncomingViewingSecretKey } from '@azt
import type { ExecutionPayload, TxExecutionRequest } from '@aztec/stdlib/tx';

import type { CallIntent, IntentInnerHash } from '../utils/authwit.js';
import type { Account, Salt } from './index.js';
import type { Account } from './index.js';

/**
* Extends {@link BaseAccount} with the encryption private key. Not required for
Expand All @@ -19,8 +19,6 @@ export class AccountWithSecretKey implements Account {
constructor(
private account: Account,
private secretKey: Fr,
/** Deployment salt for this account contract. */
public readonly salt: Salt,
) {}

createTxExecutionRequest(
Expand Down
72 changes: 59 additions & 13 deletions yarn-project/aztec.js/src/contract/deploy_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ export type DeployInstantiationOptions = {
universalDeploy?: boolean;
/** Public keys mixed into the address. Defaults to PublicKeys.default(). */
publicKeys?: PublicKeys;
/**
* Commitment to the contract's immutable storage values. Folded into the salted initialization
* hash, so a non-zero value affects the derived address. Defaults to `Fr.ZERO`.
*/
immutablesHash?: Fr;
};

/**
Expand All @@ -79,6 +84,11 @@ type SharedInstantiationOptions = {
salt?: Fr;
/** Public keys mixed into the address. Defaults to `PublicKeys.default()`. */
publicKeys?: PublicKeys;
/**
* Commitment to the contract's immutable storage values. Folded into the salted initialization
* hash, so a non-zero value affects the derived address. Defaults to `Fr.ZERO`.
*/
immutablesHash?: Fr;
};

/**
Expand Down Expand Up @@ -242,6 +252,8 @@ export abstract class DeployMethod<TContract extends ContractBase = ContractBase
protected readonly salt: Fr;
/** Public keys mixed into the address preimage. */
protected readonly publicKeys: PublicKeys;
/** Immutables hash folded into the salted initialization hash. */
protected readonly immutablesHash: Fr;

/** Cached instance promise; resolved once the deployer is known. */
#instancePromise?: Promise<ContractInstanceWithAddress>;
Expand All @@ -264,6 +276,7 @@ export abstract class DeployMethod<TContract extends ContractBase = ContractBase
contract: DeployMethodContract<TContract>,
salt: Fr | undefined,
publicKeys: PublicKeys | undefined,
immutablesHash: Fr | undefined,
payload: DeployMethodPayload = {},
) {
super(wallet, payload.authWitnesses ?? [], payload.capsules ?? []);
Expand All @@ -273,6 +286,7 @@ export abstract class DeployMethod<TContract extends ContractBase = ContractBase
this.constructorArtifact = getInitializer(contract.artifact, contract.constructorNameOrArtifact);
this.salt = salt ?? Fr.random();
this.publicKeys = publicKeys ?? PublicKeys.default();
this.immutablesHash = immutablesHash ?? Fr.ZERO;
this.extraHashedArgs = payload.extraHashedArgs ?? [];
}

Expand Down Expand Up @@ -324,19 +338,24 @@ export abstract class DeployMethod<TContract extends ContractBase = ContractBase
if (instantiation.deployer !== undefined && instantiation.universalDeploy) {
throw new Error('DeployInstantiationOptions: `deployer` and `universalDeploy` are mutually exclusive.');
}
const { salt, publicKeys, deployer, universalDeploy } = instantiation;
const { salt, publicKeys, immutablesHash, deployer, universalDeploy } = instantiation;
if (universalDeploy) {
return new UniversalDeployMethod<TContract>(
wallet,
contract,
{ salt, publicKeys, universalDeploy: true },
{ salt, publicKeys, immutablesHash, universalDeploy: true },
payload,
);
}
if (deployer !== undefined) {
return new BoundDeployMethod<TContract>(wallet, contract, { salt, publicKeys, deployer }, payload);
return new BoundDeployMethod<TContract>(
wallet,
contract,
{ salt, publicKeys, immutablesHash, deployer },
payload,
);
}
return new PendingDeployMethod<TContract>(wallet, contract, { salt, publicKeys }, payload);
return new PendingDeployMethod<TContract>(wallet, contract, { salt, publicKeys, immutablesHash }, payload);
}

/**
Expand Down Expand Up @@ -540,6 +559,7 @@ export abstract class DeployMethod<TContract extends ContractBase = ContractBase
constructorArgs: this.args,
salt: this.salt,
publicKeys: this.publicKeys,
immutablesHash: this.immutablesHash,
constructorArtifact: this.constructorArtifact,
deployer,
}).then(instance => {
Expand Down Expand Up @@ -676,7 +696,7 @@ export class BoundDeployMethod<TContract extends ContractBase = ContractBase> ex
'BoundDeployMethod requires a non-zero `deployer`; use `UniversalDeployMethod` (`{ universalDeploy: true }`) for universal deploys.',
);
}
super(wallet, contract, instantiation.salt, instantiation.publicKeys, payload);
super(wallet, contract, instantiation.salt, instantiation.publicKeys, instantiation.immutablesHash, payload);
this.deployer = instantiation.deployer;
}

Expand All @@ -703,7 +723,12 @@ export class BoundDeployMethod<TContract extends ContractBase = ContractBase> ex

/** Re-emits this method's `DeployInstantiationOptions` for `with(...)` to consume. */
public cloneInstantiation(): DeployInstantiationOptions {
return { salt: this.salt, publicKeys: this.publicKeys, deployer: this.deployer };
return {
salt: this.salt,
publicKeys: this.publicKeys,
immutablesHash: this.immutablesHash,
deployer: this.deployer,
};
}
}

Expand All @@ -720,7 +745,7 @@ export class UniversalDeployMethod<TContract extends ContractBase = ContractBase
) {
// `UniversalInstantiationOptions` forbids `deployer` and requires `universalDeploy: true` at
// the type level — no runtime check is needed.
super(wallet, contract, instantiation.salt, instantiation.publicKeys, payload);
super(wallet, contract, instantiation.salt, instantiation.publicKeys, instantiation.immutablesHash, payload);
}

/** Universal deploys are anchored at `AztecAddress.ZERO`; the sender does not enter the preimage. */
Expand All @@ -738,7 +763,12 @@ export class UniversalDeployMethod<TContract extends ContractBase = ContractBase

/** Re-emits this method's `DeployInstantiationOptions` for `with(...)` to consume. */
public cloneInstantiation(): DeployInstantiationOptions {
return { salt: this.salt, publicKeys: this.publicKeys, universalDeploy: true };
return {
salt: this.salt,
publicKeys: this.publicKeys,
immutablesHash: this.immutablesHash,
universalDeploy: true,
};
}
}

Expand Down Expand Up @@ -766,7 +796,7 @@ export class PendingDeployMethod<TContract extends ContractBase = ContractBase>
instantiation: PendingInstantiationOptions = {},
payload: DeployMethodPayload = {},
) {
super(wallet, contract, instantiation.salt, instantiation.publicKeys, payload);
super(wallet, contract, instantiation.salt, instantiation.publicKeys, instantiation.immutablesHash, payload);
}

/**
Expand Down Expand Up @@ -802,7 +832,7 @@ export class PendingDeployMethod<TContract extends ContractBase = ContractBase>
/** Re-emits this method's `DeployInstantiationOptions` for `with(...)` to consume. */
public cloneInstantiation(): DeployInstantiationOptions {
if (!this.#locked) {
return { salt: this.salt, publicKeys: this.publicKeys };
return { salt: this.salt, publicKeys: this.publicKeys, immutablesHash: this.immutablesHash };
}
return this.#locked.cloneInstantiation();
}
Expand All @@ -815,7 +845,18 @@ export class PendingDeployMethod<TContract extends ContractBase = ContractBase>
#promoteFrom(
from: SendInteractionOptionsWithoutWait['from'] | undefined,
): BoundDeployMethod<TContract> | UniversalDeployMethod<TContract> {
const { wallet, artifact, postDeployCtor, args, salt, publicKeys, authWitnesses, capsules, extraHashedArgs } = this;
const {
wallet,
artifact,
postDeployCtor,
args,
salt,
publicKeys,
immutablesHash,
authWitnesses,
capsules,
extraHashedArgs,
} = this;
const contract: DeployMethodContract<TContract> = {
artifact,
postDeployCtor,
Expand All @@ -827,10 +868,15 @@ export class PendingDeployMethod<TContract extends ContractBase = ContractBase>
return new UniversalDeployMethod<TContract>(
wallet,
contract,
{ salt, publicKeys, universalDeploy: true },
{ salt, publicKeys, immutablesHash, universalDeploy: true },
payload,
);
}
return new BoundDeployMethod<TContract>(wallet, contract, { salt, publicKeys, deployer: from }, payload);
return new BoundDeployMethod<TContract>(
wallet,
contract,
{ salt, publicKeys, immutablesHash, deployer: from },
payload,
);
}
}
40 changes: 30 additions & 10 deletions yarn-project/aztec.js/src/wallet/account_manager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Fr } from '@aztec/foundation/curves/bn254';
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
import {
CompleteAddress,
type ContractInstanceWithAddress,
Expand All @@ -13,6 +14,21 @@ import { Contract } from '../contract/contract.js';
import { DeployAccountMethod } from './deploy_account_method.js';
import type { Wallet } from './wallet.js';

/**
* Optional overrides passed to {@link AccountManager.create}.
*/
export interface AccountManagerCreateOptions {
/** Contract instantiation salt. Defaults to a random `Fr`. */
salt?: Salt;
/**
* Commitment to the contract's immutable storage values. Folded into the salted initialization
* hash, so a non-zero value affects the derived address. Defaults to `Fr.ZERO`.
*/
immutablesHash?: Fr;
/** Address recorded as the instance deployer. Defaults to `AztecAddress.ZERO`. */
deployer?: AztecAddress;
}

/**
* Manages a user account. Provides methods for calculating the account's address and other related data,
* plus a helper to return a preconfigured deploy method.
Expand All @@ -23,15 +39,16 @@ export class AccountManager {
private secretKey: Fr,
private accountContract: AccountContract,
private instance: ContractInstanceWithAddress,
/**
* Contract instantiation salt for the account contract
*/
public readonly salt: Salt,
) {}

static async create(wallet: Wallet, secretKey: Fr, accountContract: AccountContract, salt?: Salt) {
static async create(
wallet: Wallet,
secretKey: Fr,
accountContract: AccountContract,
opts?: AccountManagerCreateOptions,
) {
const { publicKeys } = await deriveKeys(secretKey);
salt = salt !== undefined ? new Fr(salt) : Fr.random();
const salt = opts?.salt !== undefined ? new Fr(opts.salt) : Fr.random();

const { constructorName, constructorArgs } = (await accountContract.getInitializationFunctionAndArgs()) ?? {
constructorName: undefined,
Expand All @@ -42,11 +59,13 @@ export class AccountManager {
const instance = await getContractInstanceFromInstantiationParams(artifact, {
constructorArtifact: constructorName,
constructorArgs,
salt: salt,
salt,
publicKeys,
deployer: opts?.deployer,
immutablesHash: opts?.immutablesHash,
});

return new AccountManager(wallet, secretKey, accountContract, instance, salt);
return new AccountManager(wallet, secretKey, accountContract, instance);
}

protected getPublicKeys() {
Expand Down Expand Up @@ -94,7 +113,7 @@ export class AccountManager {
public async getAccount(): Promise<AccountWithSecretKey> {
const completeAddress = await this.getCompleteAddress();
const account = this.accountContract.getAccount(completeAddress);
return new AccountWithSecretKey(account, this.secretKey, this.salt);
return new AccountWithSecretKey(account, this.secretKey);
}

/**
Expand Down Expand Up @@ -130,7 +149,8 @@ export class AccountManager {
this.wallet,
artifact,
instance => Contract.at(instance.address, artifact, this.wallet),
new Fr(this.salt),
this.instance.salt,
this.instance.immutablesHash,
account,
constructorArgs,
constructorName,
Expand Down
4 changes: 3 additions & 1 deletion yarn-project/aztec.js/src/wallet/deploy_account_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export class DeployAccountMethod<TContract extends ContractBase = Contract> exte
artifact: ContractArtifact,
postDeployCtor: (instance: ContractInstanceWithAddress, wallet: Wallet) => TContract,
salt: Fr,
immutablesHash: Fr,
private account: Account,
args: any[] = [],
constructorNameOrArtifact?: string | FunctionArtifact,
Expand All @@ -96,7 +97,7 @@ export class DeployAccountMethod<TContract extends ContractBase = Contract> exte
wallet,
{ artifact, postDeployCtor, args, constructorNameOrArtifact },
// Account contracts are always deployed universally.
{ salt, universalDeploy: true, publicKeys },
{ salt, universalDeploy: true, publicKeys, immutablesHash },
{ authWitnesses, capsules, extraHashedArgs },
);
}
Expand Down Expand Up @@ -221,6 +222,7 @@ export class DeployAccountMethod<TContract extends ContractBase = Contract> exte
this.artifact,
this.postDeployCtor,
this.salt,
this.immutablesHash,
this.account,
this.args,
this.constructorArtifact?.name,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/cli-wallet/src/utils/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class CLIWallet extends BaseWallet {
}

private async createAccount(secret: Fr, salt: Fr, contract: AccountContract): Promise<AccountManager> {
const accountManager = await AccountManager.create(this, secret, contract, salt);
const accountManager = await AccountManager.create(this, secret, contract, { salt });

const instance = accountManager.getInstance();
const artifact = await contract.getContractArtifact();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ export class ClientFlowsBenchmark {
// Register benchy on the user's Wallet, where we're going to be interacting from
const accountManager = await this.userWallet.createAccount({
secret: benchysAccount.getSecretKey(),
salt: new Fr(benchysAccount.salt),
salt: new Fr(benchysAccountManager.getInstance().salt),
contract: benchysAccountManager.getAccountContract(),
});
return accountManager.address;
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/spartan/block_capacity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe('block capacity benchmark', () => {
wallet,
secret,
new SchnorrAccountContract(deriveSigningKey(secret)),
salt,
{ salt },
);
const deployMethod = await manager.getDeployMethod();
await deployMethod.send({
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/spartan/n_tps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ describe('sustained N TPS test', () => {
wallet,
secret,
new SchnorrAccountContract(deriveSigningKey(secret)),
salt,
{ salt },
);
const deployMethod = await manager.getDeployMethod();
// Explicit gas estimation: BaseWallet's fallback bakes
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ describe(`prove ${TARGET_TPS}TPS test`, () => {
wallet,
secret,
new SchnorrAccountContract(deriveSigningKey(secret)),
salt,
{ salt },
);
const deployMethod = await manager.getDeployMethod();
await deployMethod.send({
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/test-wallet/test_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class TestWallet extends BaseWallet {
const type = accountData?.type ?? 'schnorr';
const contract = accountData?.contract ?? new SchnorrAccountContract(GrumpkinScalar.random());

const accountManager = await AccountManager.create(this, secret, contract, salt);
const accountManager = await AccountManager.create(this, secret, contract, { salt });

const instance = accountManager.getInstance();
const artifact = await contract.getContractArtifact();
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/wallets/src/embedded/embedded_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ export class EmbeddedWallet extends BaseWallet {
}
}

const accountManager = await AccountManager.create(this, secret, contract, salt);
const accountManager = await AccountManager.create(this, secret, contract, { salt });

const instance = accountManager.getInstance();
const existingInstance = await this.pxe.getContractInstance(instance.address);
Expand Down
Loading