diff --git a/boxes/boxes/react/src/contracts/src/main.nr b/boxes/boxes/react/src/contracts/src/main.nr index 342ad0aa62a..f46cfc2ce94 100644 --- a/boxes/boxes/react/src/contracts/src/main.nr +++ b/boxes/boxes/react/src/contracts/src/main.nr @@ -8,6 +8,7 @@ contract BoxReact { } #[aztec(private)] + #[aztec(initializer)] fn constructor(number: Field, owner: AztecAddress) { let numbers = storage.numbers; let mut new_number = ValueNote::new(number, owner); diff --git a/boxes/boxes/vanilla/src/contracts/src/main.nr b/boxes/boxes/vanilla/src/contracts/src/main.nr index fb08ae0a550..e779d259835 100644 --- a/boxes/boxes/vanilla/src/contracts/src/main.nr +++ b/boxes/boxes/vanilla/src/contracts/src/main.nr @@ -8,6 +8,7 @@ contract Vanilla { } #[aztec(private)] + #[aztec(initializer)] fn constructor(number: Field, owner: AztecAddress) { let numbers = storage.numbers; let mut new_number = ValueNote::new(number, owner); diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 1ae42fced3b..ca9e0d18c16 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -6,6 +6,24 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. +## 0.27.0 + +### `initializer` macro replaces `constructor` + +Before this version, every contract was required to have exactly one `constructor` private function, that was used for deployment. We have now removed this requirement, and made `constructor` a function like any other. + +To signal that a function can be used to **initialize** a contract, you must now decorate it with the `#[aztec(initializer)]` attribute. Initializers are regular functions that set an "initialized" flag (a nullifier) for the contract. A contract can only be initialized once, and contract functions can only be called after the contract has been initialized, much like a constructor. However, if a contract defines no initializers, it can be called at any time. Additionally, you can define as many initializer functions in a contract as you want, both private and public. + +To migrate from current code, simply add an initializer attribute to your constructor functions. + +```diff ++ #[aztec(initializer)] +#[aztec(private)] +fn constructor() { ... } +``` + +If your private constructor was used to just call a public internal initializer, then remove the private constructor and flag the public function as initializer. And if your private constructor was an empty one, just remove it. + ## 0.25.0 ### [Aztec.nr] Static calls diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 253bb31f9cd..71b296280b7 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -92,7 +92,7 @@ library Constants { uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = - 0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044; + 0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3; uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17; uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20; uint256 internal constant GET_NOTE_ORACLE_RETURN_LENGTH = 23; diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 3aaf19173b7..2b06b4b8e80 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -29,9 +29,6 @@ contract AvmTest { // avm lib use dep::aztec::avm::hash::{keccak256, poseidon, sha256}; - #[aztec(private)] - fn constructor() {} - struct Storage { single: PublicMutable, list: PublicMutable, diff --git a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr index 9b55d8005de..adf70bd0678 100644 --- a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr @@ -18,9 +18,6 @@ contract Benchmarking { balances: Map>, } - #[aztec(private)] - fn constructor() {} - // Creates a new value note for the target owner. Use this method to seed an initial set of notes. #[aztec(private)] fn create_note(owner: AztecAddress, value: Field) { diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr index 5757bcbe552..789614346ec 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr @@ -18,9 +18,6 @@ contract CardGame { games: Map>, } - #[aztec(private)] - fn constructor() {} - #[aztec(private)] fn buy_pack(seed: Field // The randomness used to generate the cards. Passed in for now. ) { diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index 1017c6e30fd..52b4c845362 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -13,9 +13,6 @@ contract Child { a_private_value: PrivateSet, } - #[aztec(private)] - fn constructor() {} - // Returns a sum of the input and the chain id and version of the contract in private circuit public input's return_values. #[aztec(private)] fn value(input: Field) -> Field { diff --git a/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr b/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr index 06127fd9b10..c86fc04e8da 100644 --- a/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr @@ -16,20 +16,9 @@ contract Claim { reward_token: SharedImmutable, } - #[aztec(private)] - fn constructor(target_contract: AztecAddress, reward_token: AztecAddress) { - let selector = FunctionSelector::from_signature("_initialize((Field),(Field))"); - context.call_public_function( - context.this_address(), - selector, - [target_contract.to_field(), reward_token.to_field()] - ); - } - #[aztec(public)] - #[aztec(internal)] - #[aztec(noinitcheck)] - fn _initialize(target_contract: AztecAddress, reward_token: AztecAddress) { + #[aztec(initializer)] + fn constructor(target_contract: AztecAddress, reward_token: AztecAddress) { storage.target_contract.initialize(target_contract); storage.reward_token.initialize(reward_token); } diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr index d1f72aa53e6..e365ef80613 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr @@ -22,9 +22,6 @@ contract ContractClassRegisterer { use crate::capsule::pop_capsule; - #[aztec(private)] - fn constructor() {} - #[aztec(private)] fn register(artifact_hash: Field, private_functions_root: Field, public_bytecode_commitment: Field) { // TODO: Validate public_bytecode_commitment is the correct commitment of packed_public_bytecode diff --git a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr index 44d2ff60514..44d3022ac3f 100644 --- a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr @@ -11,9 +11,6 @@ contract ContractInstanceDeployer { use crate::events::{instance_deployed::ContractInstanceDeployed}; - #[aztec(private)] - fn constructor() {} - #[aztec(private)] fn deploy( salt: Field, diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index 54e3bec7244..fd846f832a5 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -15,7 +15,8 @@ contract Counter { // docs:start:constructor #[aztec(private)] #[aztec(initializer)] - fn constructor(headstart: u64, owner: AztecAddress) { + // We can name our initializer anything we want as long as it's marked as aztec(initializer) + fn initialize(headstart: u64, owner: AztecAddress) { let counters = storage.counters; counters.at(owner).add(headstart, owner); } @@ -23,7 +24,7 @@ contract Counter { // docs:start:increment #[aztec(private)] - fn increment(owner: AztecAddress) { + fn increment(owner: AztecAddress) { let counters = storage.counters; counters.at(owner).add(1, owner); } diff --git a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr index fa32c7ca920..f2ff913076a 100644 --- a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr @@ -33,24 +33,13 @@ contract Crowdfunding { claim_notes: PrivateSet, } - #[aztec(private)] - fn constructor(donation_token: AztecAddress, operator: AztecAddress, deadline: u64) { - let selector = FunctionSelector::from_signature("_initialize((Field),(Field),Field)"); - context.call_public_function( - context.this_address(), - selector, - [donation_token.to_field(), operator.to_field(), deadline as Field] - ); - } - #[aztec(public)] - #[aztec(internal)] - #[aztec(noinitcheck)] - // TODO(#4990): Make deadline a u64 once the neccessary traits are implemented - fn _initialize(donation_token: AztecAddress, operator: AztecAddress, deadline: Field) { + #[aztec(initializer)] + fn constructor(donation_token: AztecAddress, operator: AztecAddress, deadline: u64) { + // TODO(#4990): Make deadline a u64 once the neccessary traits are implemented storage.donation_token.initialize(donation_token); storage.operator.initialize(operator); - storage.deadline.initialize(deadline); + storage.deadline.initialize(deadline as Field); } #[aztec(public)] diff --git a/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr b/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr index d8e1370dbe9..53460138436 100644 --- a/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr @@ -13,9 +13,6 @@ contract DelegatedOn { a_private_value: PrivateSet, } - #[aztec(private)] - fn constructor() {} - #[aztec(private)] fn private_set_value(new_value: Field, owner: AztecAddress) -> Field { let mut note = ValueNote::new(new_value, owner); diff --git a/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr b/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr index 34a82765004..7d21c033785 100644 --- a/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr @@ -12,9 +12,6 @@ contract Delegator { a_private_value: PrivateSet, } - #[aztec(private)] - fn constructor() {} - #[aztec(private)] fn private_delegate_set_value( targetContract: AztecAddress, diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 0c3fe155477..9df766144de 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -93,9 +93,6 @@ contract DocsExample { } } - #[aztec(private)] - fn constructor() {} - #[aztec(public)] fn initialize_shared_immutable(points: u8) { let mut new_leader = Leader { account: context.msg_sender(), points }; diff --git a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr index 83fd519e47b..a6348ae618e 100644 --- a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr @@ -10,9 +10,6 @@ contract GasToken { balances: Map>, } - #[aztec(private)] - fn constructor() {} - #[aztec(public)] fn claim_public(to: AztecAddress, amount: Field, canceller: EthAddress, secret: Field) { let content_hash = get_bridge_gas_msg_hash(to, amount, canceller); diff --git a/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr index 375bc2edaa5..8b5d9dec486 100644 --- a/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr @@ -11,11 +11,6 @@ contract ImportTest { ManyNotesADeepStructTestCodeGenStruct }; - // TODO(@spalladino): Delete all empty constructors - #[aztec(private)] - fn constructor( - ) {} - // Calls the testCodeGen on the Test contract at the target address // Used for testing calling a function with arguments of multiple types // See yarn-project/simulator/src/client/private_execution.ts diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index dccf19cd04c..4c7f8ea594c 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -2,9 +2,6 @@ contract Parent { use dep::aztec::prelude::{AztecAddress, FunctionSelector}; - #[aztec(private)] - fn constructor() {} - // Private function to call another private function in the targetContract using the provided selector #[aztec(private)] fn entryPoint(targetContract: AztecAddress, targetSelector: FunctionSelector) -> Field { diff --git a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr index 6983d1db955..a46df1a92db 100644 --- a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr @@ -18,9 +18,6 @@ contract PendingNoteHashes { // TODO(dbanks12): consolidate code into internal helper functions // (once Noir's support for this is more robust) - #[aztec(private)] - fn constructor() {} - // Confirm can access pending note hashes by creating / inserting a note and then // getting / reading that note all in the same contract function // Realistic way to describe this test is "Mint note A, then burn note A in the same transaction" diff --git a/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr b/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr index 4741a390917..c18c6628c85 100644 --- a/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr @@ -10,9 +10,6 @@ contract PriceFeed { assets: Map>, } - #[aztec(private)] - fn constructor() {} - #[aztec(public)] fn set_price(asset_id: Field, price: Field) { let asset = storage.assets.at(asset_id); diff --git a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr index 4fbb4a9e6ba..b35850f33d2 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr @@ -14,9 +14,6 @@ contract SchnorrHardcodedAccount { global ACCOUNT_ACTIONS_STORAGE_SLOT = 1; - #[aztec(private)] - fn constructor() {} - // Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts #[aztec(private)] fn entrypoint(app_payload: pub AppPayload, fee_payload: pub FeePayload) { diff --git a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr index d1372e30ba0..7e08267af34 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr @@ -11,9 +11,6 @@ contract SchnorrSingleKeyAccount { global ACCOUNT_ACTIONS_STORAGE_SLOT = 1; - #[aztec(private)] - fn constructor() {} - // Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts #[aztec(private)] fn entrypoint(app_payload: pub AppPayload, fee_payload: pub FeePayload) { diff --git a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr index 3c3ab166eb9..b2ded7de843 100644 --- a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr @@ -33,8 +33,6 @@ contract SlowTree { } // docs:end:constants_and_storage - #[aztec(private)] - fn constructor() {} // docs:start:initialize #[aztec(public)] fn initialize() { diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 34943b7cedc..79b14924663 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -38,12 +38,6 @@ contract Test { example_set: PrivateSet, } - // TODO(@spalladino): Delete all empty constructors - #[aztec(private)] - // docs:start:empty-constructor - fn constructor() {} - // docs:end:empty-constructor - #[aztec(private)] fn get_public_key(address: AztecAddress) -> [Field; 2] { let pub_key = get_public_key_oracle(address); diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index 811e3e20cd1..78c4386dc82 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -24,9 +24,6 @@ contract Uniswap { // gets incremented each time after use to prevent replay attacks nonce_for_burn_approval: PublicMutable, } - - #[aztec(private)] - fn constructor() {} // docs:end:uniswap_setup // docs:start:swap_public diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 51d114115ca..6e47713b6d2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -130,7 +130,7 @@ global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af8166354 // CONTRACT INSTANCE CONSTANTS // sha224sum 'struct ContractInstanceDeployed' global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; -global DEPLOYER_CONTRACT_ADDRESS = 0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044; +global DEPLOYER_CONTRACT_ADDRESS = 0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3; // NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts // Some are defined here because Noir doesn't yet support globals referencing other globals yet. diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 1f3546cbb6a..e0100977eee 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -179,15 +179,6 @@ fn transform_module( crate_graph.root_file_id, )); } - - let constructor_defined = module.functions.iter().any(|func| func.name() == "constructor"); - if !constructor_defined { - let crate_graph = &context.crate_graph[crate_id]; - return Err(( - AztecMacroError::ContractConstructorMissing { span: Span::default() }, - crate_graph.root_file_id, - )); - } } Ok(has_transformed_module) diff --git a/noir/noir-repo/aztec_macros/src/utils/errors.rs b/noir/noir-repo/aztec_macros/src/utils/errors.rs index 63892b58af9..199473baec6 100644 --- a/noir/noir-repo/aztec_macros/src/utils/errors.rs +++ b/noir/noir-repo/aztec_macros/src/utils/errors.rs @@ -7,7 +7,6 @@ use super::constants::MAX_CONTRACT_PRIVATE_FUNCTIONS; pub enum AztecMacroError { AztecDepNotFound, ContractHasTooManyPrivateFunctions { span: Span }, - ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, @@ -29,11 +28,6 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, - AztecMacroError::ContractConstructorMissing { span } => MacroError { - primary_message: "Contract must have a constructor function".to_owned(), - secondary_message: None, - span: Some(span), - }, AztecMacroError::UnsupportedFunctionArgumentType { span, typ } => MacroError { primary_message: format!("Provided parameter type `{typ:?}` is not supported in Aztec contract interface"), secondary_message: None, diff --git a/yarn-project/accounts/src/defaults/account_contract.ts b/yarn-project/accounts/src/defaults/account_contract.ts index eed3e159e6c..fe270c5acf7 100644 --- a/yarn-project/accounts/src/defaults/account_contract.ts +++ b/yarn-project/accounts/src/defaults/account_contract.ts @@ -11,7 +11,7 @@ import { DefaultAccountInterface } from '../defaults/account_interface.js'; */ export abstract class DefaultAccountContract implements AccountContract { abstract getAuthWitnessProvider(address: CompleteAddress): AuthWitnessProvider; - abstract getDeploymentArgs(): any[]; + abstract getDeploymentArgs(): any[] | undefined; constructor(private artifact: ContractArtifact) {} diff --git a/yarn-project/accounts/src/single_key/account_contract.ts b/yarn-project/accounts/src/single_key/account_contract.ts index 5af79176758..4e7b70f3284 100644 --- a/yarn-project/accounts/src/single_key/account_contract.ts +++ b/yarn-project/accounts/src/single_key/account_contract.ts @@ -18,8 +18,8 @@ export class SingleKeyAccountContract extends DefaultAccountContract { super(SchnorrSingleKeyAccountContractArtifact as ContractArtifact); } - getDeploymentArgs(): any[] { - return []; + getDeploymentArgs(): undefined { + return undefined; } getAuthWitnessProvider({ partialAddress }: CompleteAddress): AuthWitnessProvider { diff --git a/yarn-project/accounts/src/testing/create_account.ts b/yarn-project/accounts/src/testing/create_account.ts index 2cb1c356f7c..04b1af1027d 100644 --- a/yarn-project/accounts/src/testing/create_account.ts +++ b/yarn-project/accounts/src/testing/create_account.ts @@ -10,7 +10,7 @@ import { getSchnorrAccount } from '../schnorr/index.js'; * @returns - A wallet for a fresh account. */ export function createAccount(pxe: PXE): Promise { - return getSchnorrAccount(pxe, GrumpkinScalar.random(), GrumpkinScalar.random()).waitDeploy(); + return getSchnorrAccount(pxe, GrumpkinScalar.random(), GrumpkinScalar.random()).waitSetup(); } /** diff --git a/yarn-project/aztec.js/src/account/contract.ts b/yarn-project/aztec.js/src/account/contract.ts index 5f697cdbdfb..f85afacf4b7 100644 --- a/yarn-project/aztec.js/src/account/contract.ts +++ b/yarn-project/aztec.js/src/account/contract.ts @@ -16,9 +16,9 @@ export interface AccountContract { getContractArtifact(): ContractArtifact; /** - * Returns the deployment arguments for this instance. + * Returns the deployment arguments for this instance, or undefined if this contract does not require deployment. */ - getDeploymentArgs(): any[]; + getDeploymentArgs(): any[] | undefined; /** * Returns the account interface for this account contract given a deployment at the provided address. diff --git a/yarn-project/aztec.js/src/account_manager/index.ts b/yarn-project/aztec.js/src/account_manager/index.ts index 7fcfa80f807..8349f7b6458 100644 --- a/yarn-project/aztec.js/src/account_manager/index.ts +++ b/yarn-project/aztec.js/src/account_manager/index.ts @@ -26,7 +26,6 @@ export class AccountManager { private completeAddress?: CompleteAddress; private instance?: ContractInstanceWithAddress; private encryptionPublicKey?: PublicKey; - // TODO(@spalladino): Update to the new deploy method and kill the legacy one. private deployMethod?: DeployMethod; constructor( @@ -124,8 +123,10 @@ export class AccountManager { */ public async getDeployMethod() { if (!this.deployMethod) { - if (!this.salt) { - throw new Error(`Cannot deploy account contract without known salt.`); + if (!this.isDeployable()) { + throw new Error( + `Account contract ${this.accountContract.getContractArtifact().name} does not require deployment.`, + ); } await this.#register(); const encryptionPublicKey = this.getEncryptionPublicKey(); @@ -138,7 +139,7 @@ export class AccountManager { deployWallet, encryptionPublicKey, ); - const args = this.accountContract.getDeploymentArgs(); + const args = this.accountContract.getDeploymentArgs() ?? []; this.deployMethod = deployer.deploy(...args); } return this.deployMethod; @@ -146,10 +147,8 @@ export class AccountManager { /** * Deploys the account contract that backs this account. - * Does not register the associated class nor publicly deploy the instance. + * Does not register the associated class nor publicly deploy the instance by default. * Uses the salt provided in the constructor or a randomly generated one. - * Note that if the Account is constructed with an explicit complete address - * it is assumed that the account contract has already been deployed and this method will throw. * Registers the account in the PXE Service before deploying the contract. * @returns A SentTx object that can be waited to get the associated Wallet. */ @@ -165,19 +164,24 @@ export class AccountManager { } /** - * Deploys the account contract that backs this account and awaits the tx to be mined. - * Uses the salt provided in the constructor or a randomly generated one. - * Note that if the Account is constructed with an explicit complete address - * it is assumed that the account contract has already been deployed and this method will throw. - * Registers the account in the PXE Service before deploying the contract. + * Deploys the account contract that backs this account if needed and awaits the tx to be mined. + * Uses the salt provided in the constructor or a randomly generated one. If no initialization + * is required it skips the transaction, and only registers the account in the PXE Service. * @param opts - Options to wait for the tx to be mined. * @returns A Wallet instance. */ - public async waitDeploy(opts: WaitOpts = DefaultWaitOpts): Promise { - await this.deploy().then(tx => tx.wait(opts)); + public async waitSetup(opts: WaitOpts = DefaultWaitOpts): Promise { + await (this.isDeployable() ? this.deploy().then(tx => tx.wait(opts)) : this.register()); return this.getWallet(); } + /** + * Returns whether this account contract has a constructor and needs deployment. + */ + public isDeployable() { + return this.accountContract.getDeploymentArgs() !== undefined; + } + async #register(): Promise { const completeAddress = this.getCompleteAddress(); await this.pxe.registerAccount(this.encryptionPrivateKey, completeAddress.partialAddress); diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index 474cf57ede5..6639de3401e 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -41,6 +41,7 @@ describe('Contract Class', () => { functions: [ { name: 'bar', + isInitializer: false, functionType: FunctionType.SECRET, isInternal: false, debugSymbols: '', @@ -65,6 +66,7 @@ describe('Contract Class', () => { }, { name: 'baz', + isInitializer: false, functionType: FunctionType.OPEN, isInternal: false, parameters: [], @@ -74,6 +76,7 @@ describe('Contract Class', () => { }, { name: 'qux', + isInitializer: false, functionType: FunctionType.UNCONSTRAINED, isInternal: false, parameters: [ diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 3766e6e6bf5..78121c55ef1 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -5,7 +5,7 @@ import { getContractClassFromArtifact, getContractInstanceFromDeployParams, } from '@aztec/circuits.js'; -import { ContractArtifact, FunctionArtifact } from '@aztec/foundation/abi'; +import { ContractArtifact, FunctionArtifact, getDefaultInitializer } from '@aztec/foundation/abi'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -33,8 +33,10 @@ export type DeployOptions = { universalDeploy?: boolean; /** Skip contract class registration. */ skipClassRegistration?: boolean; - /** Skip public deployment and only initialize the contract. */ + /** Skip public deployment, instead just privately initialize the contract. */ skipPublicDeployment?: boolean; + /** Skip contract initialization. */ + skipInitialization?: boolean; } & SendMethodOptions; // TODO(@spalladino): Add unit tests for this class! @@ -48,7 +50,10 @@ export class DeployMethod extends Bas private instance?: ContractInstanceWithAddress = undefined; /** Constructor function to call. */ - private constructorArtifact: FunctionArtifact; + private constructorArtifact: FunctionArtifact | undefined; + + /** Cached call to request() */ + private functionCalls: FunctionCall[] | undefined; private log = createDebugLogger('aztec:js:deploy_method'); @@ -58,14 +63,16 @@ export class DeployMethod extends Bas private artifact: ContractArtifact, private postDeployCtor: (address: AztecAddress, wallet: Wallet) => Promise, private args: any[] = [], - constructorName: string = 'constructor', + constructorName?: string, ) { super(wallet); - const constructorArtifact = artifact.functions.find(f => f.name === constructorName); - if (!constructorArtifact) { - throw new Error('Cannot find constructor in the artifact.'); + this.constructorArtifact = constructorName + ? artifact.functions.find(f => f.name === constructorName) + : getDefaultInitializer(artifact); + + if (constructorName && !this.constructorArtifact) { + throw new Error(`Constructor method ${constructorName} not found in contract artifact`); } - this.constructorArtifact = constructorArtifact; } /** @@ -79,6 +86,10 @@ export class DeployMethod extends Bas */ public async create(options: DeployOptions = {}): Promise { if (!this.txRequest) { + const calls = await this.request(options); + if (calls.length === 0) { + throw new Error(`No function calls needed to deploy contract ${this.artifact.name}`); + } this.txRequest = await this.wallet.createTxExecutionRequest(await this.request(options)); // TODO: Should we add the contracts to the DB here, or once the tx has been sent or mined? await this.pxe.addContracts([{ artifact: this.artifact, instance: this.instance! }]); @@ -95,9 +106,21 @@ export class DeployMethod extends Bas * it returns a promise for an array instead of a function call directly. */ public async request(options: DeployOptions = {}): Promise { - const { address } = this.getInstance(options); - const constructorCall = new ContractFunctionInteraction(this.wallet, address, this.constructorArtifact, this.args); - return [...(await this.getDeploymentFunctionCalls(options)), constructorCall.request()]; + if (!this.functionCalls) { + const { address } = this.getInstance(options); + const calls = await this.getDeploymentFunctionCalls(options); + if (this.constructorArtifact && !options.skipInitialization) { + const constructorCall = new ContractFunctionInteraction( + this.wallet, + address, + this.constructorArtifact, + this.args, + ); + calls.push(constructorCall.request()); + } + this.functionCalls = calls; + } + return this.functionCalls; } /** @@ -168,7 +191,7 @@ export class DeployMethod extends Bas salt: options.contractAddressSalt, portalAddress: options.portalContract, publicKey: this.publicKey, - constructorName: this.constructorArtifact.name, + constructorArtifact: this.constructorArtifact, }); } return this.instance; diff --git a/yarn-project/aztec.js/src/wallet/account_wallet.ts b/yarn-project/aztec.js/src/wallet/account_wallet.ts index b8db6836ff4..5cbe9df38d3 100644 --- a/yarn-project/aztec.js/src/wallet/account_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/account_wallet.ts @@ -63,6 +63,7 @@ export class AccountWallet extends BaseWallet { private getApprovePublicAuthwitAbi(): FunctionAbi { return { name: 'approve_public_authwit', + isInitializer: false, functionType: FunctionType.OPEN, isInternal: true, parameters: [ @@ -79,6 +80,7 @@ export class AccountWallet extends BaseWallet { private getCancelAuthwitAbi(): FunctionAbi { return { name: 'cancel_authwit', + isInitializer: false, functionType: FunctionType.SECRET, isInternal: true, parameters: [ diff --git a/yarn-project/aztec/src/examples/token.ts b/yarn-project/aztec/src/examples/token.ts index e04e04e24b9..582b4616c55 100644 --- a/yarn-project/aztec/src/examples/token.ts +++ b/yarn-project/aztec/src/examples/token.ts @@ -25,8 +25,8 @@ const TRANSFER_AMOUNT = 33n; async function main() { logger('Running token contract test on HTTP interface.'); - aliceWallet = await getSingleKeyAccount(pxe, alicePrivateKey).waitDeploy(); - bobWallet = await getSingleKeyAccount(pxe, bobPrivateKey).waitDeploy(); + aliceWallet = await getSingleKeyAccount(pxe, alicePrivateKey).waitSetup(); + bobWallet = await getSingleKeyAccount(pxe, bobPrivateKey).waitSetup(); const alice = aliceWallet.getCompleteAddress(); const bob = bobWallet.getCompleteAddress(); diff --git a/yarn-project/circuit-types/src/contract_dao.test.ts b/yarn-project/circuit-types/src/contract_dao.test.ts index 7f212a51bfe..e8bfe08be0c 100644 --- a/yarn-project/circuit-types/src/contract_dao.test.ts +++ b/yarn-project/circuit-types/src/contract_dao.test.ts @@ -17,6 +17,7 @@ describe('ContractDao', () => { functions: [ { name: 'bar', + isInitializer: false, functionType: FunctionType.SECRET, isInternal: false, parameters: [ diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 866d0ab9fd9..e26ba5f0a9a 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -77,7 +77,7 @@ export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99n; export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631n; -export const DEPLOYER_CONTRACT_ADDRESS = 0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044n; +export const DEPLOYER_CONTRACT_ADDRESS = 0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3n; export const L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17; export const MAX_NOTE_FIELDS_LENGTH = 20; export const GET_NOTE_ORACLE_RETURN_LENGTH = 23; diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap index b633b08695d..d1997e34518 100644 --- a/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap @@ -35,17 +35,10 @@ exports[`ContractClass creates a contract class from a contract compilation arti }, "vkHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "isInternal": false - }, - { - "selector": { - "value": 2432309179 - }, - "vkHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "isInternal": false } ], - "id": "0x02dc42f5fd77ab2b01b47ead12ab4b22ca490c9b337d5a37954da383778551f9", - "privateFunctionsRoot": "0x05fa82a96814b6294d557d507151f7ccc12f70522ec4d9d0395a90e87e8087c6", + "id": "0x2e0b68846797a66afbe01df9748a70b801f95398ae0a7a578d087f2db6a4d57f", + "privateFunctionsRoot": "0x2dc1f38d7be98a8e72227d6f8aec393c60db813a1819c9c86b02a00cc18f6687", "publicBytecodeCommitment": "0x00b38e2fc3eb0f8520f32355d7195aaa2a137dc2f8d2a69748830a05da2e5e5a" }" `; diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts index 84c3517fc4b..9ff2f8d54a1 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -5,7 +5,7 @@ describe('ArtifactHash', () => { it('calculates the artifact hash', () => { const artifact = getSampleContractArtifact(); expect(computeArtifactHash(artifact).toString()).toMatchInlineSnapshot( - `"0x2136048d7b91f63060c3dc03417c0b2835eac99ab393a87fc6e4ccfb3d65e5bc"`, + `"0x10d144027c5d0dddb7336f9becb14db882c0f4e48cfab674f1871458f81838ca"`, ); }); }); diff --git a/yarn-project/circuits.js/src/contract/contract_address.test.ts b/yarn-project/circuits.js/src/contract/contract_address.test.ts index 961636cfadf..36c0ae2f91f 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.test.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.test.ts @@ -36,6 +36,7 @@ describe('ContractAddress', () => { it('computeInitializationHash', () => { const mockInitFn: FunctionAbi = { functionType: FunctionType.SECRET, + isInitializer: false, isInternal: false, name: 'fun', parameters: [{ name: 'param1', type: { kind: 'boolean' }, visibility: ABIParameterVisibility.SECRET }], @@ -46,6 +47,11 @@ describe('ContractAddress', () => { expect(result).toMatchSnapshot(); }); + it('computeInitializationHash empty', () => { + const result = computeInitializationHash(undefined, []); + expect(result).toEqual(Fr.ZERO); + }); + it('computeContractAddressFromInstance', () => { const publicKey = new Point(new Fr(1n), new Fr(2n)); const salt = new Fr(3n); diff --git a/yarn-project/circuits.js/src/contract/contract_address.ts b/yarn-project/circuits.js/src/contract/contract_address.ts index 933cbe6207d..e9892ae461c 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.ts @@ -92,11 +92,14 @@ export function computePublicKeysHash(publicKey: PublicKey | undefined): Fr { /** * Computes the initialization hash for an instance given its constructor function and arguments. - * @param initFn - Constructor function. + * @param initFn - Constructor function or empty if no initialization is expected. * @param args - Unencoded arguments, will be encoded as fields according to the constructor function abi. - * @returns The hash. + * @returns The hash, or zero if no initialization function is provided. */ -export function computeInitializationHash(initFn: FunctionAbi, args: any[]): Fr { +export function computeInitializationHash(initFn: FunctionAbi | undefined, args: any[]): Fr { + if (!initFn) { + return Fr.ZERO; + } const selector = FunctionSelector.fromNameAndParameters(initFn.name, initFn.parameters); const flatArgs = encodeArguments(initFn, args); return computeInitializationHashFromEncodedArgs(selector, flatArgs); diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts index b6e829fefd5..e30d12beb08 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -1,4 +1,4 @@ -import { ContractArtifact } from '@aztec/foundation/abi'; +import { ContractArtifact, FunctionArtifact, getDefaultInitializer } from '@aztec/foundation/abi'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { ContractInstance, ContractInstanceWithAddress } from '@aztec/types/contracts'; @@ -21,7 +21,7 @@ import { export function getContractInstanceFromDeployParams( artifact: ContractArtifact, opts: { - constructorName?: string; + constructorArtifact?: FunctionArtifact | string; constructorArgs?: any[]; salt?: Fr; publicKey?: PublicKey; @@ -32,15 +32,7 @@ export function getContractInstanceFromDeployParams( const salt = opts.salt ?? Fr.random(); const publicKey = opts.publicKey ?? Point.ZERO; const portalContractAddress = opts.portalAddress ?? EthAddress.ZERO; - const constructorName = opts.constructorName ?? 'constructor'; - - const constructorArtifact = artifact.functions.find(fn => fn.name === constructorName); - if (!constructorArtifact) { - throw new Error(`Cannot find constructor with name ${constructorName} in the artifact.`); - } - if (!constructorArtifact.verificationKey) { - throw new Error('Missing verification key for the constructor.'); - } + const constructorArtifact = getConstructorArtifact(artifact, opts.constructorArtifact); const contractClass = getContractClassFromArtifact(artifact); const contractClassId = computeContractClassId(contractClass); @@ -58,3 +50,17 @@ export function getContractInstanceFromDeployParams( return { ...instance, address: computeContractAddressFromInstance(instance) }; } + +function getConstructorArtifact( + artifact: ContractArtifact, + requestedConstructorArtifact: FunctionArtifact | string | undefined, +): FunctionArtifact | undefined { + if (typeof requestedConstructorArtifact === 'string') { + const found = artifact.functions.find(fn => fn.name === requestedConstructorArtifact); + if (!found) { + throw new Error(`No constructor found with name ${requestedConstructorArtifact}`); + } + return found; + } + return requestedConstructorArtifact ?? getDefaultInitializer(artifact); +} diff --git a/yarn-project/cli/src/test/mocks.ts b/yarn-project/cli/src/test/mocks.ts index 7a9e662eda6..b1463de3acd 100644 --- a/yarn-project/cli/src/test/mocks.ts +++ b/yarn-project/cli/src/test/mocks.ts @@ -5,6 +5,7 @@ export const mockContractArtifact: ContractArtifact = { functions: [ { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ @@ -22,6 +23,7 @@ export const mockContractArtifact: ContractArtifact = { }, { name: 'mockFunction', + isInitializer: false, functionType: FunctionType.SECRET, isInternal: false, parameters: [ diff --git a/yarn-project/end-to-end/src/docs_examples.test.ts b/yarn-project/end-to-end/src/docs_examples.test.ts index 12d1fb9c0ab..93eab76cfcb 100644 --- a/yarn-project/end-to-end/src/docs_examples.test.ts +++ b/yarn-project/end-to-end/src/docs_examples.test.ts @@ -18,7 +18,7 @@ const pxe = createPXEClient(PXE_URL); // docs:end:define_account_vars // docs:start:create_wallet -const wallet = await getSchnorrAccount(pxe, encryptionPrivateKey, signingPrivateKey).waitDeploy(); +const wallet = await getSchnorrAccount(pxe, encryptionPrivateKey, signingPrivateKey).waitSetup(); // docs:end:create_wallet // docs:start:deploy_contract diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index d5613bc9a41..aaeb216120a 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -258,7 +258,7 @@ describe('e2e_2_pxes', () => { const privateKey = GrumpkinScalar.random(); const account = getUnsafeSchnorrAccount(pxeA, privateKey, Fr.random()); const completeAddress = account.getCompleteAddress(); - const wallet = await account.waitDeploy(); + const wallet = await account.waitSetup(); await expect(wallet.isAccountStateSynchronized(completeAddress.address)).resolves.toBe(true); const accountOnB = getUnsafeSchnorrAccount(pxeB, privateKey, account.salt); @@ -318,7 +318,7 @@ describe('e2e_2_pxes', () => { const sharedPrivateKey = GrumpkinScalar.random(); const sharedAccountOnA = getUnsafeSchnorrAccount(pxeA, sharedPrivateKey, Fr.random()); const sharedAccountAddress = sharedAccountOnA.getCompleteAddress(); - const sharedWalletOnA = await sharedAccountOnA.waitDeploy(); + const sharedWalletOnA = await sharedAccountOnA.waitSetup(); await expect(sharedWalletOnA.isAccountStateSynchronized(sharedAccountAddress.address)).resolves.toBe(true); const sharedAccountOnB = getUnsafeSchnorrAccount(pxeB, sharedPrivateKey, sharedAccountOnA.salt); diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index 4cd7d629f2f..e3149ec793c 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -70,7 +70,7 @@ function itShouldBehaveLikeAnAccountContract( describe('e2e_account_contracts', () => { const walletSetup = async (pxe: PXE, encryptionPrivateKey: GrumpkinPrivateKey, accountContract: AccountContract) => { const account = new AccountManager(pxe, encryptionPrivateKey, accountContract); - return await account.deploy().then(tx => tx.getWallet()); + return await account.waitSetup(); }; const walletAt = async (pxe: PXE, accountContract: AccountContract, address: CompleteAddress) => { diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 2a982b4c8ef..f26b8cb1726 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -14,7 +14,8 @@ import { } from '@aztec/aztec.js'; import { times } from '@aztec/foundation/collection'; import { pedersenHash } from '@aztec/foundation/crypto'; -import { TestContract, TestContractArtifact } from '@aztec/noir-contracts.js/Test'; +import { StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { setup } from './fixtures/utils.js'; @@ -28,7 +29,7 @@ describe('e2e_block_building', () => { let teardown: () => Promise; describe('multi-txs block', () => { - const artifact = TestContractArtifact; + const artifact = StatefulTestContractArtifact; beforeAll(async () => { ({ @@ -49,7 +50,7 @@ describe('e2e_block_building', () => { const TX_COUNT = 8; await aztecNode.setConfig({ minTxsPerBlock: TX_COUNT }); const deployer = new ContractDeployer(artifact, owner); - const methods = times(TX_COUNT, () => deployer.deploy()); + const methods = times(TX_COUNT, i => deployer.deploy(owner.getCompleteAddress().address, i)); for (let i = 0; i < TX_COUNT; i++) { await methods[i].create({ contractAddressSalt: new Fr(BigInt(i + 1)), diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index adff32ba032..6e26b459fad 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -111,7 +111,7 @@ describe('e2e_card_game', () => { logger(`Deploying account contract ${i}/${toRegister.length}...`); const encryptionPrivateKey = toRegister[i]; const account = getSchnorrAccount(pxe, encryptionPrivateKey, GrumpkinScalar.random()); - const wallet = await account.waitDeploy({ interval: 0.1 }); + const wallet = await account.waitSetup({ interval: 0.1 }); wallets.push(wallet); } logger('Account contracts deployed'); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index 244e77c64dc..d74ca9a26a3 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -27,7 +27,7 @@ import { import { ContractClassIdPreimage, Point } from '@aztec/circuits.js'; import { siloNullifier } from '@aztec/circuits.js/hash'; import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; -import { StatefulTestContract } from '@aztec/noir-contracts.js'; +import { CounterContract, StatefulTestContract } from '@aztec/noir-contracts.js'; import { TestContract, TestContractArtifact } from '@aztec/noir-contracts.js/Test'; import { TokenContract, TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; import { SequencerClient } from '@aztec/sequencer-client'; @@ -312,7 +312,7 @@ describe('e2e_deploy_contract', () => { salt, publicKey, portalAddress, - constructorName: opts.constructorName, + constructorArtifact: opts.constructorName, }); const { address, contractClassId } = instance; logger(`Deploying contract instance at ${address.toString()} class id ${contractClassId.toString()}`); @@ -462,8 +462,10 @@ describe('e2e_deploy_contract', () => { it('publicly deploys and initializes a contract', async () => { const owner = accounts[0]; + logger.debug(`Deploying stateful test contract`); const contract = await StatefulTestContract.deploy(wallet, owner, 42).send().deployed(); expect(await contract.methods.summed_values(owner).view()).toEqual(42n); + logger.debug(`Calling public method on stateful test contract at ${contract.address.toString()}`); await contract.methods.increment_public_value(owner, 84).send().wait(); expect(await contract.methods.get_public_value(owner).view()).toEqual(84n); }, 60_000); @@ -486,6 +488,30 @@ describe('e2e_deploy_contract', () => { expect(await contract.methods.summed_values(owner).view()).toEqual(30n); }, 60_000); + it('deploys a contract with a default initializer not named constructor', async () => { + logger.debug(`Deploying contract with a default initializer named initialize`); + const opts = { skipClassRegistration: true, skipPublicDeployment: true }; + const contract = await CounterContract.deploy(wallet, 10, accounts[0]).send(opts).deployed(); + logger.debug(`Calling a function to ensure the contract was properly initialized`); + await contract.methods.increment(accounts[0]).send().wait(); + expect(await contract.methods.get_counter(accounts[0]).view()).toEqual(11n); + }); + + it('publicly deploys a contract with no constructor', async () => { + logger.debug(`Deploying contract with no constructor`); + const contract = await TestContract.deploy(wallet).send().deployed(); + logger.debug(`Call a public function to check that it was publicly deployed`); + const receipt = await contract.methods.emit_unencrypted(42).send().wait(); + const logs = await pxe.getUnencryptedLogs({ txHash: receipt.txHash }); + expect(logs.logs[0].log.data.toString('hex').replace(/^0+/, '')).toEqual('2a'); + }); + + it('refuses to deploy a contract with no constructor and no public deployment', async () => { + logger.debug(`Deploying contract with no constructor and skipping public deploy`); + const opts = { skipPublicDeployment: true, skipClassRegistration: true }; + await expect(TestContract.deploy(wallet).simulate(opts)).rejects.toThrow(/no function calls needed/i); + }); + it.skip('publicly deploys and calls a public function in the same batched call', async () => { // TODO(@spalladino): Requires being able to read a nullifier on the same tx it was emitted. }); @@ -517,7 +543,7 @@ async function registerContract( const { salt, publicKey, portalAddress, initArgs, constructorName } = opts; const instance = getContractInstanceFromDeployParams(contractArtifact.artifact, { constructorArgs: initArgs ?? [], - constructorName, + constructorArtifact: constructorName, salt, publicKey, portalAddress, diff --git a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts index e8abe983d96..bc01605d6b4 100644 --- a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts +++ b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts @@ -40,7 +40,7 @@ describe('e2e_multiple_accounts_1_enc_key', () => { logger(`Deploying account contract ${i}/3...`); const signingPrivateKey = GrumpkinScalar.random(); const account = getSchnorrAccount(pxe, encryptionPrivateKey, signingPrivateKey); - const wallet = await account.waitDeploy({ interval: 0.1 }); + const wallet = await account.waitSetup({ interval: 0.1 }); const completeAddress = account.getCompleteAddress(); wallets.push(wallet); accounts.push(completeAddress); diff --git a/yarn-project/end-to-end/src/e2e_persistence.test.ts b/yarn-project/end-to-end/src/e2e_persistence.test.ts index f52f4c42d07..8ddeda7d039 100644 --- a/yarn-project/end-to-end/src/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/e2e_persistence.test.ts @@ -60,7 +60,7 @@ describe('Aztec persistence', () => { deployL1ContractsValues = initialContext.deployL1ContractsValues; ownerPrivateKey = Fq.random(); - const ownerWallet = await getUnsafeSchnorrAccount(initialContext.pxe, ownerPrivateKey, Fr.ZERO).waitDeploy(); + const ownerWallet = await getUnsafeSchnorrAccount(initialContext.pxe, ownerPrivateKey, Fr.ZERO).waitSetup(); ownerAddress = ownerWallet.getCompleteAddress(); ownerSalt = ownerWallet.salt; @@ -149,7 +149,7 @@ describe('Aztec persistence', () => { }); it('allows spending of private notes', async () => { - const otherWallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitDeploy(); + const otherWallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitSetup(); const initialOwnerBalance = await contract.methods.balance_of_private(ownerWallet.getAddress()).view(); @@ -197,7 +197,7 @@ describe('Aztec persistence', () => { it('pxe does not know of the deployed contract', async () => { await context.pxe.registerRecipient(ownerAddress); - const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitDeploy(); + const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitSetup(); await expect(TokenContract.at(contractAddress, wallet)).rejects.toThrow(/has not been registered/); }); @@ -210,7 +210,7 @@ describe('Aztec persistence', () => { ]); await context.pxe.registerRecipient(ownerAddress); - const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitDeploy(); + const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitSetup(); const contract = await TokenContract.at(contractAddress, wallet); await expect(contract.methods.balance_of_private(ownerAddress.address).view()).resolves.toEqual(0n); }); @@ -223,7 +223,7 @@ describe('Aztec persistence', () => { }, ]); - const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitDeploy(); + const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitSetup(); const contract = await TokenContract.at(contractAddress, wallet); await expect(contract.methods.total_supply().view()).resolves.toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts index 9445da155d9..a3c5867d403 100644 --- a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts +++ b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts @@ -196,7 +196,7 @@ describe('e2e_sandbox_example', () => { ); return await Promise.all( accountManagers.map(async x => { - await x.waitDeploy({}); + await x.waitSetup({}); return x; }), ); diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index 10618ba5dea..01ddc35f00b 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -26,9 +26,9 @@ class SchnorrHardcodedKeyAccountContract extends DefaultAccountContract { super(SchnorrHardcodedAccountContractArtifact); } - getDeploymentArgs(): any[] { - // This contract does not require any arguments in its constructor. - return []; + getDeploymentArgs(): undefined { + // This contract has no constructor + return undefined; } getAuthWitnessProvider(_address: CompleteAddress): AuthWitnessProvider { @@ -58,7 +58,7 @@ describe('guides/writing_an_account_contract', () => { // docs:start:account-contract-deploy const encryptionPrivateKey = GrumpkinScalar.random(); const account = new AccountManager(pxe, encryptionPrivateKey, new SchnorrHardcodedKeyAccountContract()); - const wallet = await account.waitDeploy(); + const wallet = await account.waitSetup(); const address = wallet.getCompleteAddress().address; // docs:end:account-contract-deploy logger(`Deployed account contract at ${address}`); diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index 94b57e45c77..ee6e973d9e0 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -120,7 +120,7 @@ export const browserTestSuite = ( const pxe = createPXEClient(rpcUrl!); const privateKey = GrumpkinScalar.fromString(privateKeyString); const account = getUnsafeSchnorrAccount(pxe, privateKey); - await account.waitDeploy(); + await account.waitSetup(); const completeAddress = account.getCompleteAddress(); const addressString = completeAddress.address.toString(); console.log(`Created Account: ${addressString}`); @@ -186,7 +186,7 @@ export const browserTestSuite = ( getUnsafeSchnorrAccount, } = window.AztecJs; const pxe = createPXEClient(rpcUrl!); - const newReceiverAccount = await getUnsafeSchnorrAccount(pxe, AztecJs.GrumpkinScalar.random()).waitDeploy(); + const newReceiverAccount = await getUnsafeSchnorrAccount(pxe, AztecJs.GrumpkinScalar.random()).waitSetup(); const receiverAddress = newReceiverAccount.getCompleteAddress().address; const [wallet] = await getDeployedTestAccountsWallets(pxe); const contract = await Contract.at(AztecAddress.fromString(contractAddress), TokenContractArtifact, wallet); @@ -232,7 +232,7 @@ export const browserTestSuite = ( INITIAL_TEST_ENCRYPTION_KEYS[0], INITIAL_TEST_SIGNING_KEYS[0], INITIAL_TEST_ACCOUNT_SALTS[0], - ).waitDeploy(); + ).waitSetup(); knownAccounts.push(newAccount); } const owner = knownAccounts[0]; diff --git a/yarn-project/entrypoints/src/account_entrypoint.ts b/yarn-project/entrypoints/src/account_entrypoint.ts index 1dd3762b5d6..ebb8f772d2b 100644 --- a/yarn-project/entrypoints/src/account_entrypoint.ts +++ b/yarn-project/entrypoints/src/account_entrypoint.ts @@ -43,6 +43,7 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { private getEntrypointAbi() { return { name: 'entrypoint', + isInitializer: false, functionType: 'secret', isInternal: false, parameters: [ diff --git a/yarn-project/entrypoints/src/dapp_entrypoint.ts b/yarn-project/entrypoints/src/dapp_entrypoint.ts index 810b25e6ae2..392d08512ba 100644 --- a/yarn-project/entrypoints/src/dapp_entrypoint.ts +++ b/yarn-project/entrypoints/src/dapp_entrypoint.ts @@ -51,6 +51,7 @@ export class DefaultDappEntrypoint implements EntrypointInterface { private getEntrypointAbi() { return { name: 'entrypoint', + isInitializer: false, functionType: 'secret', isInternal: false, parameters: [ diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index cf32e19076a..d8ba0f8a7e4 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -152,6 +152,10 @@ export interface FunctionAbi { * The types of the return values. */ returnTypes: ABIType[]; + /** + * Whether the function is flagged as an initializer. + */ + isInitializer: boolean; } /** @@ -335,3 +339,19 @@ export function getFunctionDebugMetadata( } return undefined; } + +/** + * Returns an initializer from the contract, assuming there is at least one. If there are multiple initializers, + * it returns the one named "constructor"; if there is none with that name, it returns the first private initializer + * it finds. + * @param contractArtifact - The contract artifact. + * @returns An initializer function, or none if there are no functions flagged as initializers in the contract. + */ +export function getDefaultInitializer(contractArtifact: ContractArtifact): FunctionArtifact | undefined { + const initializers = contractArtifact.functions.filter(f => f.isInitializer); + return initializers.length > 1 + ? initializers.find(f => f.name === 'constructor') ?? + initializers.find(f => f.functionType === FunctionType.SECRET) ?? + initializers[0] + : initializers[0]; +} diff --git a/yarn-project/foundation/src/abi/encoder.test.ts b/yarn-project/foundation/src/abi/encoder.test.ts index fcbbeed6267..a85c2454236 100644 --- a/yarn-project/foundation/src/abi/encoder.test.ts +++ b/yarn-project/foundation/src/abi/encoder.test.ts @@ -10,6 +10,7 @@ describe('abi/encoder', () => { name: 'constructor', functionType: FunctionType.SECRET, isInternal: false, + isInitializer: true, parameters: [ { name: 'owner', @@ -29,6 +30,7 @@ describe('abi/encoder', () => { it('serializes arrays of fields', () => { const abi: FunctionAbi = { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ @@ -52,6 +54,7 @@ describe('abi/encoder', () => { it('serializes string', () => { const abi: FunctionAbi = { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ @@ -76,6 +79,7 @@ describe('abi/encoder', () => { it.each(['AztecAddress', 'EthAddress'])('accepts address instance for %s structs', (structType: string) => { const abi: FunctionAbi = { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ @@ -110,6 +114,7 @@ describe('abi/encoder', () => { it('accepts a field for a wrapped field', () => { const abi: FunctionAbi = { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ @@ -140,6 +145,7 @@ describe('abi/encoder', () => { it('throws when passing string argument as field', () => { const testFunctionAbi: FunctionAbi = { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ @@ -161,6 +167,7 @@ describe('abi/encoder', () => { it('throws when passing string argument as integer', () => { const testFunctionAbi: FunctionAbi = { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ @@ -185,6 +192,7 @@ describe('abi/encoder', () => { it('throws when passing object argument as field', () => { const testFunctionAbi: FunctionAbi = { name: 'constructor', + isInitializer: true, functionType: FunctionType.SECRET, isInternal: false, parameters: [ diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts index 8710ceabd42..87e22fcb23e 100644 --- a/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts +++ b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts @@ -2,6 +2,7 @@ import { ABIParameter, ContractArtifact, FunctionArtifact, + getDefaultInitializer, isAztecAddressStruct, isEthAddressStruct, isFunctionSelectorStruct, @@ -71,7 +72,7 @@ function generateMethod(entry: FunctionArtifact) { * @returns A type-safe deploy method in ts. */ function generateDeploy(input: ContractArtifact) { - const ctor = input.functions.find(f => f.name === 'constructor'); + const ctor = getDefaultInitializer(input); const args = (ctor?.parameters ?? []).map(generateParameter).join(', '); const contractName = `${input.name}Contract`; const artifactName = `${contractName}Artifact`; diff --git a/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap index 8c3db32215a..1b7e5ec8cc8 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap @@ -2,10 +2,10 @@ exports[`ClassRegisterer returns canonical protocol contract 1`] = ` { - "address": AztecAddress<0x251ab40f36a148a5ac88fe3c2398ec1968d5676b0066299278610b870d738a9e>, + "address": AztecAddress<0x1ff58462bc433d2e277ca6371c796fcc2dbfae869edf87b60f7fdff335ae8baa>, "contractClass": { - "artifactHash": Fr<0x25407c4d1b24174951117458ded0d9d6daef3676f8a4f8c8c15ccc6f78658cbe>, - "id": Fr<0x0e47c799d78f73e39985a308a6418e89f9bd698655b4a63c18520cdbc439b8aa>, + "artifactHash": Fr<0x20d64bd232dd14c00fedcaf31d10ad879a63eee4183824d1cadf2dac49b1f9ce>, + "id": Fr<0x2ced3a9029e1a272aa799dd8d66c3fae1dffed2259cc96b578a4bcc997d6c7b1>, "packedBytecode": Buffer<0x00000000>, "privateFunctions": [ { @@ -18,26 +18,21 @@ exports[`ClassRegisterer returns canonical protocol contract 1`] = ` "selector": Selector<0x237fa87b>, "vkHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, }, - { - "isInternal": false, - "selector": Selector<0x90fa17bb>, - "vkHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - }, { "isInternal": false, "selector": Selector<0x98bc6593>, "vkHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, }, ], - "privateFunctionsRoot": Fr<0x07c6b6bc3cf31904d0e08d2ec1abdeed4fe8800aaeafad588531d2857c29916c>, + "privateFunctionsRoot": Fr<0x0930d0038f2076b5a41aec6905cda5f201f20b75f3f9703d2aabbaea86268ac5>, "publicBytecodeCommitment": Fr<0x1dae27cc7fe2af345f160253be3875d449a7e9ba6bd68747d87d4e7054b81115>, "publicFunctions": [], "version": 1, }, "instance": { - "address": AztecAddress<0x251ab40f36a148a5ac88fe3c2398ec1968d5676b0066299278610b870d738a9e>, - "contractClassId": Fr<0x0e47c799d78f73e39985a308a6418e89f9bd698655b4a63c18520cdbc439b8aa>, - "initializationHash": Fr<0x0bf6e812f14bb029f7cb9c8da8367dd97c068e788d4f21007fd97014eba8cf9f>, + "address": AztecAddress<0x1ff58462bc433d2e277ca6371c796fcc2dbfae869edf87b60f7fdff335ae8baa>, + "contractClassId": Fr<0x2ced3a9029e1a272aa799dd8d66c3fae1dffed2259cc96b578a4bcc997d6c7b1>, + "initializationHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, "portalContractAddress": EthAddress<0x0000000000000000000000000000000000000000>, "publicKeysHash": Fr<0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed>, "salt": Fr<0x0000000000000000000000000000000000000000000000000000000000000001>, diff --git a/yarn-project/protocol-contracts/src/class-registerer/index.ts b/yarn-project/protocol-contracts/src/class-registerer/index.ts index 11ad7901b9f..8492aace1e3 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/index.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/index.ts @@ -13,5 +13,5 @@ export function getCanonicalClassRegisterer(): ProtocolContract { * @remarks This should not change often, hence we hardcode it to save from having to recompute it every time. */ export const ClassRegistererAddress = AztecAddress.fromString( - '0x251ab40f36a148a5ac88fe3c2398ec1968d5676b0066299278610b870d738a9e', + '0x1ff58462bc433d2e277ca6371c796fcc2dbfae869edf87b60f7fdff335ae8baa', ); diff --git a/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap index 797645eae84..33361fa429d 100644 --- a/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap @@ -2,11 +2,11 @@ exports[`GasToken returns canonical protocol contract 1`] = ` { - "address": AztecAddress<0x0d73a39da86016c0ef7a2fdad3c3bf2c47d46c89ed2f0b823e21a2ad7461d043>, + "address": AztecAddress<0x1416fe0ac99c3128c1dd0f777d8dbece71e14a6a514d08ecba93844ec2d28331>, "instance": { - "address": AztecAddress<0x0d73a39da86016c0ef7a2fdad3c3bf2c47d46c89ed2f0b823e21a2ad7461d043>, - "contractClassId": Fr<0x1463ccb41e0b69bc648f16f413a5d993d70c05f7b11a5b7b9300bdcfd8b3d31f>, - "initializationHash": Fr<0x0bf6e812f14bb029f7cb9c8da8367dd97c068e788d4f21007fd97014eba8cf9f>, + "address": AztecAddress<0x1416fe0ac99c3128c1dd0f777d8dbece71e14a6a514d08ecba93844ec2d28331>, + "contractClassId": Fr<0x02b558e65089476ee9a12f77979e1f823bd88fe6f11523ffaa0af6816467e371>, + "initializationHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, "portalContractAddress": EthAddress<0x0000000000000000000000000000000000000000>, "publicKeysHash": Fr<0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed>, "salt": Fr<0x0000000000000000000000000000000000000000000000000000000000000001>, @@ -17,16 +17,10 @@ exports[`GasToken returns canonical protocol contract 1`] = ` exports[`GasToken returns canonical protocol contract 2`] = ` { - "artifactHash": Fr<0x076fb6d7493b075a880eeed90fec7c4c01e0a24d442522449e4d56c26357205f>, - "id": Fr<0x1463ccb41e0b69bc648f16f413a5d993d70c05f7b11a5b7b9300bdcfd8b3d31f>, - "privateFunctions": [ - { - "isInternal": false, - "selector": Selector<0x90fa17bb>, - "vkHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - }, - ], - "privateFunctionsRoot": Fr<0x13b29c3f4a96eb14d5d3539a6308ff9736ad5d67e3f61ffbb7da908e14980828>, + "artifactHash": Fr<0x18af4bb0ca6fe07d0ae6da493b2c7b1af038ee904721dbba9b6e571e6d495726>, + "id": Fr<0x02b558e65089476ee9a12f77979e1f823bd88fe6f11523ffaa0af6816467e371>, + "privateFunctions": [], + "privateFunctionsRoot": Fr<0x15d28cad4c0736decea8997cb324cf0a0e0602f4d74472cd977bce2c8dd9923f>, "publicBytecodeCommitment": Fr<0x079241a3e691f43ee2832dedbb09a1a1780622509a2e020dba561104758f5e01>, "version": 1, } diff --git a/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap index a562c6a022f..8f30d5d38c4 100644 --- a/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap @@ -2,10 +2,10 @@ exports[`InstanceDeployer returns canonical protocol contract 1`] = ` { - "address": AztecAddress<0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044>, + "address": AztecAddress<0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3>, "contractClass": { - "artifactHash": Fr<0x1ef6560f240eb778e8c6a3f4af609f96dd167b484714028614b0021709f8a4dc>, - "id": Fr<0x2990cefd2e58ed0fbfb97c242efe906442d8ecb57114a39f4b0b15a250859627>, + "artifactHash": Fr<0x088abf2a235b9047f92f51a30630eeec47614f5f997d42aa70bc6c20ecf27b87>, + "id": Fr<0x22905afb676c1c6736b344418266b16dbf06c2e13bdcd386617bc5b8db3103df>, "packedBytecode": Buffer<0x00000000>, "privateFunctions": [ { @@ -13,21 +13,16 @@ exports[`InstanceDeployer returns canonical protocol contract 1`] = ` "selector": Selector<0x883355ab>, "vkHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, }, - { - "isInternal": false, - "selector": Selector<0x90fa17bb>, - "vkHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, - }, ], - "privateFunctionsRoot": Fr<0x167f605d0f768f91ce61813817e60c01aa73dbc5e78574804a471e337305801b>, + "privateFunctionsRoot": Fr<0x15a91b2ada8e46d36cfdcd4e46c9c3c29f1b159a60b1ec67d8ed7a3782f89eef>, "publicBytecodeCommitment": Fr<0x1dae27cc7fe2af345f160253be3875d449a7e9ba6bd68747d87d4e7054b81115>, "publicFunctions": [], "version": 1, }, "instance": { - "address": AztecAddress<0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044>, - "contractClassId": Fr<0x2990cefd2e58ed0fbfb97c242efe906442d8ecb57114a39f4b0b15a250859627>, - "initializationHash": Fr<0x0bf6e812f14bb029f7cb9c8da8367dd97c068e788d4f21007fd97014eba8cf9f>, + "address": AztecAddress<0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3>, + "contractClassId": Fr<0x22905afb676c1c6736b344418266b16dbf06c2e13bdcd386617bc5b8db3103df>, + "initializationHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, "portalContractAddress": EthAddress<0x0000000000000000000000000000000000000000>, "publicKeysHash": Fr<0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed>, "salt": Fr<0x0000000000000000000000000000000000000000000000000000000000000001>, diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 3e26e492f2b..2df3cc58e06 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -6,7 +6,6 @@ import { FunctionData, Header, L1_TO_L2_MSG_TREE_HEIGHT, - MAX_NEW_NOTE_HASHES_PER_CALL, NOTE_HASH_TREE_HEIGHT, PartialStateReference, PublicCallRequest, @@ -210,15 +209,7 @@ describe('Private Execution test suite', () => { acirSimulator = new AcirSimulator(oracle, node); }); - describe('empty constructor', () => { - it('should run the empty constructor', async () => { - const artifact = getFunctionArtifact(TestContractArtifact, 'constructor'); - const result = await runSimulator({ artifact }); - - const emptyCommitments = new Array(MAX_NEW_NOTE_HASHES_PER_CALL).fill(Fr.ZERO); - expect(sideEffectArrayToValueArray(result.callStackItem.publicInputs.newNoteHashes)).toEqual(emptyCommitments); - }); - + describe('no constructor', () => { it('emits a field as an unencrypted log', async () => { const artifact = getFunctionArtifact(TestContractArtifact, 'emit_msg_sender'); const result = await runSimulator({ artifact, msgSender: owner }); diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 4096888503c..04f82018194 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -8,6 +8,7 @@ import { } from '@aztec/foundation/abi'; import { + AZTEC_INITIALIZER_ATTRIBUTE, AZTEC_INTERNAL_ATTRIBUTE, AZTEC_PRIVATE_ATTRIBUTE, AZTEC_PUBLIC_ATTRIBUTE, @@ -45,9 +46,7 @@ export function loadContractArtifact(input: NoirCompiledContract): ContractArtif if (isContractArtifact(input)) { return input; } - const contractArtifact = generateContractArtifact(input); - validateContractArtifact(contractArtifact); - return contractArtifact; + return generateContractArtifact(input); } /** @@ -128,6 +127,7 @@ function generateFunctionArtifact(fn: NoirCompiledContractFunction): FunctionArt name: fn.name, functionType, isInternal, + isInitializer: fn.custom_attributes.includes(AZTEC_INITIALIZER_ATTRIBUTE), parameters, returnTypes, bytecode: fn.bytecode, @@ -165,15 +165,6 @@ function hasKernelFunctionInputs(params: ABIParameter[]): boolean { return firstParam?.type.kind === 'struct' && firstParam.type.path.includes('ContextInputs'); } -/** Validates contract artifact instance, throwing on error. */ -function validateContractArtifact(contract: ContractArtifact) { - const constructorArtifact = contract.functions.find(({ name }) => name === 'constructor'); - if (constructorArtifact === undefined) { - throw new Error('Contract must have a constructor function'); - } - return contract; -} - /** * Given a Nargo output generates an Aztec-compatible contract artifact. * @param compiled - Noir build output.