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
6 changes: 3 additions & 3 deletions aztec-up/bin/0.0.1/aztec-install
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ function title {
echo -e " ${bold}${g}aztec-up${r} - a tool to install and manage aztec toolchain versions."
echo -e " ${bold}${g}aztec-wallet${r} - our minimalistic CLI wallet"
echo
read -p "Do you wish to continue? (y/n) " -n 1 -r
read -p "Do you wish to continue? (Y/n) " -n 1 -r
echo
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
if [[ ! $REPLY =~ ^[Yy]?$ ]]; then
exit 1
fi
}
Expand All @@ -83,7 +83,7 @@ function check_for_old_install {
echo_yellow "If you continue, the entire $AZTEC_HOME directory will be removed and replaced with the new installation."
echo "You should manually remove old docker images you no longer need."
echo
read -p "Do you wish to continue? (y/n) " -n 1 -r
read -p "Do you wish to continue? (y/N) " -n 1 -r
echo
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Confirm the account was deployed successfully:

```typescript title="verify_account_deployment" showLineNumbers
const metadata = await wallet.getContractMetadata(newAccount.address);
console.log("Account deployed:", metadata.isContractInitialized);
console.log("Account deployed:", metadata.initializationStatus);
```
> <sup><sub><a href="https://github.com/AztecProtocol/aztec-packages/blob/v4.0.0-nightly.20260217/docs/examples/ts/aztecjs_connection/index.ts#L86-L89" target="_blank" rel="noopener noreferrer">Source code: docs/examples/ts/aztecjs_connection/index.ts#L86-L89</a></sub></sup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ const metadata = await wallet.getContractMetadata(contractAddress);
metadata.instance; // Contract registered in your wallet?
metadata.isContractClassPubliclyRegistered; // Class registered on the network?
metadata.isContractPublished; // Instance registered on the network?
metadata.isContractInitialized; // Constructor has been called?
metadata.initializationStatus; // Constructor has been called?
```

For a complete overview of what these states mean and when functions become callable, see [Contract Readiness States](../aztec-nr/contract_readiness_states.md).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ const metadata = await wallet.getContractMetadata(contractAddress);
metadata.instance; // Contract registered in your wallet?
metadata.isContractClassPubliclyRegistered; // Class registered on the network?
metadata.isContractPublished; // Instance registered on the network?
metadata.isContractInitialized; // Constructor has been called?
metadata.initializationStatus; // Constructor has been called?
```

For a complete overview of what these states mean and when functions become callable, see [Contract Readiness States](../aztec-nr/contract_readiness_states.md).
Expand Down
23 changes: 22 additions & 1 deletion docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

### [aztec.js] `isContractInitialized` is now `initializationStatus` tri-state enum

`ContractMetadata.isContractInitialized` has been renamed to `ContractMetadata.initializationStatus` and changed from `boolean | undefined` to a `ContractInitializationStatus` enum with values `INITIALIZED`, `UNINITIALIZED`, and `UNKNOWN`.

- `INITIALIZED`: the contract has been initialized (initialization nullifier found)
- `UNINITIALIZED`: the contract instance is registered but has not been initialized
- `UNKNOWN`: the instance is not registered and no public initialization nullifier was found

When the instance is not registered, the wallet now attempts to check the public initialization nullifier (computed from address alone) before returning `UNKNOWN`. Previously this case returned `undefined`.

**Migration:**

```diff
+ import { ContractInitializationStatus } from '@aztec/aztec.js/wallet';

const metadata = await wallet.getContractMetadata(address);
- if (metadata.isContractInitialized) {
+ if (metadata.initializationStatus === ContractInitializationStatus.INITIALIZED) {
// contract is initialized
}
```

### [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`.
Expand Down Expand Up @@ -64,7 +86,6 @@ The `scope` field in `ExecuteUtilityOptions` has been renamed to `scopes` and ch
```

**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
2 changes: 1 addition & 1 deletion docs/examples/ts/aztecjs_connection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ await deployMethodFeeJuice.send({

// docs:start:verify_account_deployment
const metadata = await wallet.getContractMetadata(newAccount.address);
console.log("Account deployed:", metadata.isContractInitialized);
console.log("Account deployed:", metadata.initializationStatus);
// docs:end:verify_account_deployment

// docs:start:deploy_contract
Expand Down
6 changes: 3 additions & 3 deletions playground/src/components/navbar/components/WalletHub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
} from '@aztec/wallet-sdk/manager';
import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
import { Fr } from '@aztec/foundation/curves/bn254';
import type { Wallet } from '@aztec/aztec.js/wallet';
import { ContractInitializationStatus, type Wallet } from '@aztec/aztec.js/wallet';

type ExtendedWalletProvider = Omit<WalletProvider, 'type'> & {
type: WalletProvider['type'] | 'embedded';
Expand Down Expand Up @@ -86,8 +86,8 @@ async function discoverTestAccounts(wallet: EmbeddedWallet) {
return;
}

const { isContractInitialized } = await wallet.getContractMetadata(sampleAccount.address);
if (!isContractInitialized) {
const { initializationStatus } = await wallet.getContractMetadata(sampleAccount.address);
if (initializationStatus !== ContractInitializationStatus.INITIALIZED) {
return;
}

Expand Down
18 changes: 17 additions & 1 deletion yarn-project/.claude/rules/typescript-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,20 @@ mock.getData.mockImplementation((id: string) => {
}
return Promise.resolve(undefined);
});
```
```

## Arrow Function Bodies

Use expression bodies instead of block bodies when the block only contains a `return`:

```typescript
// Good: Expression body
items.map(item => item.value * 2)
fn(arg => expression(arg, foo))

// Bad: Block body with just a return
items.map(item => { return item.value * 2; })
fn(arg => { return expression(arg, foo); })
```

Block bodies are appropriate when the callback has multiple statements or side effects beyond the return.
34 changes: 24 additions & 10 deletions yarn-project/archiver/src/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,19 +342,33 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
return Promise.resolve(this.synchronizer.getL1Timestamp());
}

public getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
public async getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
// The synced L2 slot is the latest slot for which we have all L1 data,
// either because we have seen all L1 blocks for that slot, or because
// we have seen the corresponding checkpoint.

let slotFromL1Sync: SlotNumber | undefined;
const l1Timestamp = this.synchronizer.getL1Timestamp();
if (l1Timestamp === undefined) {
return Promise.resolve(undefined);
if (l1Timestamp !== undefined) {
const nextL1BlockSlot = getSlotAtNextL1Block(l1Timestamp, this.l1Constants);
if (Number(nextL1BlockSlot) > 0) {
slotFromL1Sync = SlotNumber.add(nextL1BlockSlot, -1);
}
}

let slotFromCheckpoint: SlotNumber | undefined;
const latestCheckpointNumber = await this.store.getSynchedCheckpointNumber();
if (latestCheckpointNumber > 0) {
const checkpointData = await this.store.getCheckpointData(latestCheckpointNumber);
if (checkpointData) {
slotFromCheckpoint = checkpointData.header.slotNumber;
}
}
// The synced slot is the last L2 slot whose all L1 blocks have been processed.
// If the next L1 block (at l1Timestamp + ethereumSlotDuration) falls in slot N,
// then we've fully synced slot N-1.
const nextL1BlockSlot = getSlotAtNextL1Block(l1Timestamp, this.l1Constants);
if (Number(nextL1BlockSlot) === 0) {
return Promise.resolve(undefined);

if (slotFromL1Sync === undefined && slotFromCheckpoint === undefined) {
return undefined;
}
return Promise.resolve(SlotNumber(nextL1BlockSlot - 1));
return SlotNumber(Math.max(slotFromL1Sync ?? 0, slotFromCheckpoint ?? 0));
}

public async getSyncedL2EpochNumber(): Promise<EpochNumber | undefined> {
Expand Down
17 changes: 12 additions & 5 deletions yarn-project/archiver/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { protocolContractNames } from '@aztec/protocol-contracts';
import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi';
import type { ArchiverEmitter } from '@aztec/stdlib/block';
import { type ContractClassPublic, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
import { type ContractClassPublicWithCommitment, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
import { getTelemetryClient } from '@aztec/telemetry-client';

Expand Down Expand Up @@ -175,14 +175,22 @@ export async function createArchiver(
return archiver;
}

/** Registers protocol contracts in the archiver store. */
/** Registers protocol contracts in the archiver store. Idempotent — skips contracts that already exist (e.g. on node restart). */
export async function registerProtocolContracts(store: KVArchiverDataStore) {
const blockNumber = 0;
for (const name of protocolContractNames) {
const provider = new BundledProtocolContractsProvider();
const contract = await provider.getProtocolContractArtifact(name);
const contractClassPublic: ContractClassPublic = {

// Skip if already registered (happens on node restart with a persisted store).
if (await store.getContractClass(contract.contractClass.id)) {
continue;
}

const publicBytecodeCommitment = await computePublicBytecodeCommitment(contract.contractClass.packedBytecode);
const contractClassPublic: ContractClassPublicWithCommitment = {
...contract.contractClass,
publicBytecodeCommitment,
privateFunctions: [],
utilityFunctions: [],
};
Expand All @@ -192,8 +200,7 @@ export async function registerProtocolContracts(store: KVArchiverDataStore) {
.map(fn => decodeFunctionSignature(fn.name, fn.parameters));

await store.registerContractFunctionSignatures(publicFunctionSignatures);
const bytecodeCommitment = await computePublicBytecodeCommitment(contractClassPublic.packedBytecode);
await store.addContractClasses([contractClassPublic], [bytecodeCommitment], BlockNumber(blockNumber));
await store.addContractClasses([contractClassPublic], BlockNumber(blockNumber));
await store.addContractInstances([contract.instance], BlockNumber(blockNumber));
}
}
42 changes: 31 additions & 11 deletions yarn-project/archiver/src/modules/data_store_updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import {
import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
import { type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
import {
type ContractClassPublicWithCommitment,
type ExecutablePrivateFunctionWithMembershipProof,
type UtilityFunctionWithMembershipProof,
computePublicBytecodeCommitment,
computeContractClassId,
isValidPrivateFunctionMembershipProof,
isValidUtilityFunctionMembershipProof,
} from '@aztec/stdlib/contract';
Expand Down Expand Up @@ -321,18 +322,37 @@ export class ArchiverDataStoreUpdater {
.filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log))
.map(log => ContractClassPublishedEvent.fromLog(log));

const contractClasses = await Promise.all(contractClassPublishedEvents.map(e => e.toContractClassPublic()));
if (contractClasses.length > 0) {
contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
if (operation == Operation.Store) {
// TODO: Will probably want to create some worker threads to compute these bytecode commitments as they are expensive
const commitments = await Promise.all(
contractClasses.map(c => computePublicBytecodeCommitment(c.packedBytecode)),
);
return await this.store.addContractClasses(contractClasses, commitments, blockNum);
} else if (operation == Operation.Delete) {
if (operation == Operation.Delete) {
const contractClasses = contractClassPublishedEvents.map(e => e.toContractClassPublic());
if (contractClasses.length > 0) {
contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
return await this.store.deleteContractClasses(contractClasses, blockNum);
}
return true;
}

// Compute bytecode commitments and validate class IDs in a single pass.
const contractClasses: ContractClassPublicWithCommitment[] = [];
for (const event of contractClassPublishedEvents) {
const contractClass = await event.toContractClassPublicWithBytecodeCommitment();
const computedClassId = await computeContractClassId({
artifactHash: contractClass.artifactHash,
privateFunctionsRoot: contractClass.privateFunctionsRoot,
publicBytecodeCommitment: contractClass.publicBytecodeCommitment,
});
if (!computedClassId.equals(contractClass.id)) {
this.log.warn(
`Skipping contract class with mismatched id at block ${blockNum}. Claimed ${contractClass.id}, computed ${computedClassId}`,
{ blockNum, contractClassId: event.contractClassId.toString() },
);
continue;
}
contractClasses.push(contractClass);
}

if (contractClasses.length > 0) {
contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
return await this.store.addContractClasses(contractClasses, blockNum);
}
return true;
}
Expand Down
10 changes: 7 additions & 3 deletions yarn-project/archiver/src/store/contract_class_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ export class ContractClassStore {
blockNumber: number,
): Promise<void> {
await this.db.transactionAsync(async () => {
await this.#contractClasses.setIfNotExists(
contractClass.id.toString(),
const key = contractClass.id.toString();
if (await this.#contractClasses.hasAsync(key)) {
throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
}
await this.#contractClasses.set(
key,
serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
);
await this.#bytecodeCommitments.setIfNotExists(contractClass.id.toString(), bytecodeCommitment.toBuffer());
await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer());
});
}

Expand Down
13 changes: 8 additions & 5 deletions yarn-project/archiver/src/store/contract_instance_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ export class ContractInstanceStore {

addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise<void> {
return this.db.transactionAsync(async () => {
await this.#contractInstances.set(
contractInstance.address.toString(),
new SerializableContractInstance(contractInstance).toBuffer(),
);
await this.#contractInstancePublishedAt.set(contractInstance.address.toString(), blockNumber);
const key = contractInstance.address.toString();
if (await this.#contractInstances.hasAsync(key)) {
throw new Error(
`Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
);
}
await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
await this.#contractInstancePublishedAt.set(key, blockNumber);
});
}

Expand Down
Loading
Loading