From 03f3aadc83f8b427bf574e2bb11cd42bf7ac1c26 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 30 Jan 2025 19:32:44 +0000 Subject: [PATCH 1/6] refactor: generalizing fee refund flow --- .../contracts/fpc_contract/src/main.nr | 41 +++++++++- .../contracts/token_contract/src/main.nr | 79 ------------------- 2 files changed, 39 insertions(+), 81 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 480358c23100..e78f34a9d7f1 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -83,10 +83,47 @@ contract FPC { // TODO(PR #8022): Once PublicImmutable performs only 1 merkle proof here, we'll save ~4k gates let config = storage.config.read(); - Token::at(config.accepted_asset).setup_refund(context.msg_sender(), max_fee, nonce).call( + let user = context.msg_sender(); + let token = Token::at(config.accepted_asset); + + token.transfer_to_public(user, context.this_address(), max_fee, nonce).call(&mut context); + let refund_slot = token.prepare_private_balance_increase(user, user).call(&mut context); + + context.set_as_fee_payer(); + + let max_fee_serialized = max_fee.serialize(); + context.set_public_teardown_function( + context.this_address(), + comptime { + FunctionSelector::from_signature("complete_refund((Field),Field,(Field,Field))") + }, + [ + config.accepted_asset.to_field(), + refund_slot, + max_fee_serialized[0], + max_fee_serialized[1], + ], + ); + } + + /// Executed as a public teardown function and is responsible for completing the refund in a private fee payment + /// flow. + #[public] + #[internal] + fn complete_refund(accepted_asset: AztecAddress, refund_slot: Field, max_fee: U128) { + let tx_fee = U128::from_integer(context.transaction_fee()); + + // 1. We check that user funded the fee payer contract with at least the transaction fee. + // TODO(#7796): we should try to prevent reverts here + assert(max_fee >= tx_fee, "max fee not enough to cover tx fee"); + + // 2. We compute the refund amount as the difference between funded amount and the tx fee. + // TODO(#10805): Introduce a real exchange rate + let refund_amount = max_fee - tx_fee; + + Token::at(accepted_asset).finalize_transfer_to_private(refund_amount, refund_slot).call( &mut context, ); - context.set_as_fee_payer(); } /// Pays for the tx fee with msg_sender's public balance of accepted asset (AA). The maximum fee a user is willing diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 4fde31bc841c..8d8f8b11d515 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -603,56 +603,6 @@ contract Token { finalization_payload.emit(); } - // docs:start:setup_refund - /// Called by fee payer contract (FPC) to set up a refund for a user during the private fee payment flow. - #[private] - fn setup_refund( - user: AztecAddress, // A user for which we are setting up the fee refund. - max_fee: U128, // The maximum fee a user is willing to pay for the tx. - nonce: Field, // A nonce to make authwitness unique. - ) { - // 1. This function is called by FPC when setting up a refund so we need to support the authwit flow here - // and check that the user really permitted fee_recipient to set up a refund on their behalf. - assert_current_call_valid_authwit(&mut context, user); - - // 2. Deduct the max fee from user's balance. The difference between max fee and the actual tx fee will - // be refunded to the user in the `complete_refund(...)` function. - let change = subtract_balance( - &mut context, - storage, - user, - max_fee, - INITIAL_TRANSFER_CALL_MAX_NOTES, - ); - // Emit the change note. - storage.balances.at(user).add(user, change).emit(encode_and_encrypt_note_unconstrained( - &mut context, - user, - user, - )); - - // 3. Prepare the partial note for the refund. - let user_point_slot = _prepare_private_balance_increase(user, user, &mut context, storage); - - // 4. Set the public teardown function to `complete_refund(...)`. Public teardown is the only time when a public - // function has access to the final transaction fee, which is needed to compute the actual refund amount. - let fee_recipient = context.msg_sender(); // FPC is the fee recipient. - let max_fee_serialized = max_fee.serialize(); - context.set_public_teardown_function( - context.this_address(), - comptime { - FunctionSelector::from_signature("complete_refund((Field),Field,(Field,Field))") - }, - [ - fee_recipient.to_field(), - user_point_slot, - max_fee_serialized[0], - max_fee_serialized[1], - ], - ); - } - // docs:end:setup_refund - // TODO(#9375): Having to define the note log length here is very unfortunate as it's basically impossible for // users to derive manually. This will however go away once we have a real transient storage since we will not need // the public call and instead we would do something like `context.transient_storage_write(slot, payload)` and that @@ -669,35 +619,6 @@ contract Token { context.storage_write(slot + aztec::protocol_types::point::POINT_LENGTH as Field, setup_log); } - // docs:start:complete_refund - /// Executed as a public teardown function and is responsible for completing the refund in a private fee payment - /// flow. - #[public] - #[internal] - fn complete_refund(fee_recipient: AztecAddress, user_slot: Field, max_fee: U128) { - let tx_fee = U128::from_integer(context.transaction_fee()); - - // 1. We check that user funded the fee payer contract with at least the transaction fee. - // TODO(#7796): we should try to prevent reverts here - assert(max_fee >= tx_fee, "max fee not enough to cover tx fee"); - - // 2. We compute the refund amount as the difference between funded amount and the tx fee. - // TODO(#10805): Introduce a real exchange rate - let refund_amount = max_fee - tx_fee; - - // 3. We send the tx fee to the fee recipient in public. - _increase_public_balance_inner(fee_recipient, tx_fee, storage); - - // 4. We construct the user note finalization payload with the refund amount. - let user_finalization_payload = - UintNote::finalization_payload().new(&mut context, user_slot, refund_amount); - - // 5. At last we emit the user finalization note hash and the corresponding note log. - user_finalization_payload.emit(); - // --> Once the tx is settled user and fee recipient can add the notes to their pixies. - } - // docs:end:complete_refund - /// Internal /// // docs:start:increase_public_balance /// TODO(#9180): Consider adding macro support for functions callable both as an entrypoint and as an internal From e6a42a15e8f97faa3be067f2fb8f887dd7c9ffe8 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 30 Jan 2025 20:12:09 +0000 Subject: [PATCH 2/6] fixes --- .../contracts/fpc_contract/src/main.nr | 55 ++++++------- .../contracts/token_contract/src/test.nr | 1 - .../token_contract/src/test/refunds.nr | 78 ------------------- .../src/fee/private_fee_payment_method.ts | 6 +- 4 files changed, 28 insertions(+), 112 deletions(-) delete mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index e78f34a9d7f1..3b1df32eb30f 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -38,16 +38,14 @@ contract FPC { /// ## Overview /// Uses partial notes to implement a refund flow which works as follows: /// Setup Phase: - /// 1. This `setup_refund` function: - /// - Calls the AA token contract, which: - /// - subtracts the `max_fee` from the user's balance; - /// - prepares a partial note for the user (which will be used to later refund the user any unspent fee); - /// - sets a public teardown function (within the same AA token contract), where at the end of the tx - /// a fee (denominated in AA) will be transferred to the FPC in public, and a partial note will be finalized - /// with the refund amount (also denominated in AA). - /// - Sets itself as the `fee_payer` of the tx; meaning this contract will be responsible for ultimately - /// transferring the `tx_fee` -- denominated in fee juice -- to the protocol, during the later "teardown" - /// phase of this tx. + /// 1. This `fee_entrypoint_private` function: + /// - Transfers `max_fee` of AA from private balance of the user to the public balance of this contract, + /// - prepares a partial note for the user (which will later on be used to refund the user any unspent fee), + /// - sets a public teardown function, where at the end of the tx a partial note will be finalized + /// with the refund amount (also denominated in AA), + /// - sets itself as the `fee_payer` of the tx; meaning this contract will be responsible for ultimately + /// transferring the `tx_fee` -- denominated in fee juice -- to the protocol, during the later "teardown" + /// phase of this tx. /// /// Execution Phase: /// 2. Then the private and public functions of the tx get executed. @@ -56,12 +54,10 @@ contract FPC { /// 3. By this point, the protocol has computed the `tx_fee` (denominated in "fee juice"). So now we can /// execute the "teardown function" which was lined-up during the earlier "setup phase". /// Within the teardown function, we: - /// - compute how much of the `max_fee` (denominated in AA) the user needs to pay to the FPC, - /// and how much of it will be refunded back to the user. Since the protocol-calculated `tx_fee` is - /// denominated in fee juice, and not in this FPC's AA, an equivalent value of AA is computed based - /// on an exchange rate between AA and fee juice. - /// - finalize the refund note with a value of `max_fee - tx_fee` for the user; - /// - send the tx fee to the FPC in public. + /// - Compute how much of the `max_fee` (denominated in AA) will be refunded back to the user. Since + /// the protocol-calculated `tx_fee` is denominated in fee juice, and not in this FPC's AA, an equivalent + /// value of AA is computed based on the exchange rate between AA and fee juice, + /// - finalize the refund note with a value of `max_fee - tx_fee` for the user. /// /// Protocol-enshrined fee-payment phase: /// 4. The protocol deducts the protocol-calculated `tx_fee` (denominated in fee juice) from the `fee_payer`'s @@ -69,41 +65,40 @@ contract FPC { /// "fee juice" contract. /// /// With this scheme a user has privately paid for the tx fee with an arbitrary AA (e.g. could be a stablecoin), - /// by paying this FPC. This FPC has in turn paid the protocol-mandated `tx_fee` (denominated in fee - /// juice). + /// by paying this FPC. This FPC has in turn paid the protocol-mandated `tx_fee` (denominated in fee juice). /// /// ***Note:*** /// This flow allows us to pay for the tx with msg_sender's private balance of AA and hence msg_sender's identity /// is not revealed. We do, however, reveal: - /// - the `max_fee`; - /// - which FPC has been used to make the payment; + /// - the `max_fee`, + /// - which FPC has been used to make the payment, /// - the asset which was used to make the payment. #[private] fn fee_entrypoint_private(max_fee: U128, nonce: Field) { // TODO(PR #8022): Once PublicImmutable performs only 1 merkle proof here, we'll save ~4k gates - let config = storage.config.read(); + let accepted_asset = storage.config.read().accepted_asset; let user = context.msg_sender(); - let token = Token::at(config.accepted_asset); + let token = Token::at(accepted_asset); + // Pull the max fee from the user's balance of the accepted asset to the public balance of this contract. token.transfer_to_public(user, context.this_address(), max_fee, nonce).call(&mut context); - let refund_slot = token.prepare_private_balance_increase(user, user).call(&mut context); - context.set_as_fee_payer(); + // Prepare a partial note for the refund for the user. + let refund_slot = token.prepare_private_balance_increase(user, user).call(&mut context); + // Set a public teardown function in which the refund will be paid back to the user by finalizing the partial note. let max_fee_serialized = max_fee.serialize(); context.set_public_teardown_function( context.this_address(), comptime { FunctionSelector::from_signature("complete_refund((Field),Field,(Field,Field))") }, - [ - config.accepted_asset.to_field(), - refund_slot, - max_fee_serialized[0], - max_fee_serialized[1], - ], + [accepted_asset.to_field(), refund_slot, max_fee_serialized[0], max_fee_serialized[1]], ); + + // Set the FPC as the fee payer of the tx. + context.set_as_fee_payer(); } /// Executed as a public teardown function and is responsible for completing the refund in a private fee payment diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr index 1ed2b177ced8..6317752f169a 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr @@ -3,7 +3,6 @@ mod burn_private; mod burn_public; mod mint_to_public; mod reading_constants; -mod refunds; mod transfer; mod transfer_in_private; mod transfer_in_public; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr deleted file mode 100644 index 39396840dcbf..000000000000 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr +++ /dev/null @@ -1,78 +0,0 @@ -use crate::{test::utils, Token}; -use aztec::oracle::random::random; -use aztec::protocol_types::abis::gas::Gas; -use aztec::protocol_types::abis::gas_fees::GasFees; - -use dep::authwit::cheatcodes as authwit_cheatcodes; -use std::test::OracleMock; - -#[test] -unconstrained fn setup_refund_success() { - // Gas used to compute transaction fee - // TXE oracle uses gas_used = Gas(1,1) when crafting TX - let txe_expected_gas_used = Gas::new(1, 1); - // TXE oracle uses gas fees of (1, 1) - let txe_gas_fees = GasFees::new(1, 1); - let expected_tx_fee = U128::from_integer(txe_expected_gas_used.compute_fee(txe_gas_fees)); - - // Fund account with enough to cover tx fee plus some - let funded_amount = U128::from_integer(1_000) + expected_tx_fee; - - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_amount_to_private(true, funded_amount); - - // Renaming owner and recipient to match naming in Token - let user = owner; - let fee_payer = recipient; - - let nonce = 123; - - let setup_refund_from_call_interface = - Token::at(token_contract_address).setup_refund(user, funded_amount, nonce); - - authwit_cheatcodes::add_private_authwit_from_call_interface( - user, - fee_payer, - setup_refund_from_call_interface, - ); - - env.impersonate(fee_payer); - - setup_refund_from_call_interface.call(&mut env.private()); - - env.advance_block_by(1); - - utils::check_public_balance(token_contract_address, fee_payer, expected_tx_fee); - utils::check_private_balance(token_contract_address, user, mint_amount - expected_tx_fee); -} - -// This test should be reworked when final support for partial notes is in -// Once that happens, the expected error message is commented out below -//#[test(should_fail_with="max fee not enough to cover tx fee")] -#[test(should_fail_with = "Balance too low")] -unconstrained fn setup_refund_insufficient_funded_amount() { - let (env, token_contract_address, owner, recipient, _mint_amount) = - utils::setup_and_mint_to_private(true); - - // Renaming owner and recipient to match naming in Token - let user = owner; - let fee_payer = recipient; - - // We set funded amount to 0 to make the transaction fee higher than the funded amount - let funded_amount = U128::zero(); - let nonce = random(); - - let setup_refund_from_call_interface = - Token::at(token_contract_address).setup_refund(user, funded_amount, nonce); - - authwit_cheatcodes::add_private_authwit_from_call_interface( - user, - fee_payer, - setup_refund_from_call_interface, - ); - - env.impersonate(fee_payer); - - // The following should fail with "max fee not enough to cover tx fee" because funded amount is 0 - setup_refund_from_call_interface.call(&mut env.private()) -} diff --git a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts index ab032e9c2269..db40ad162001 100644 --- a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts +++ b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts @@ -94,9 +94,9 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod { await this.wallet.createAuthWit({ caller: this.paymentContract, action: { - name: 'setup_refund', - args: [this.wallet.getAddress().toField(), ...maxFee.toFields(), nonce], - selector: await FunctionSelector.fromSignature('setup_refund((Field),(Field,Field),Field)'), + name: 'transfer_to_public', + args: [this.wallet.getAddress().toField(), this.paymentContract.toField(), ...maxFee.toFields(), nonce], + selector: await FunctionSelector.fromSignature('transfer_to_public((Field),(Field),(Field,Field),Field)'), type: FunctionType.PRIVATE, isStatic: false, to: await this.getAsset(), From 0d65630cbe652cdecb8fe9e929d4effa75276fc2 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 30 Jan 2025 20:22:41 +0000 Subject: [PATCH 3/6] docs fix --- docs/docs/aztec/concepts/storage/partial_notes.md | 9 +++++---- .../codealong/contract_tutorials/token_contract.md | 12 ------------ .../contracts/fpc_contract/src/main.nr | 4 ++++ 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/docs/docs/aztec/concepts/storage/partial_notes.md b/docs/docs/aztec/concepts/storage/partial_notes.md index 20f2826cab8a..ba5f7196f5fb 100644 --- a/docs/docs/aztec/concepts/storage/partial_notes.md +++ b/docs/docs/aztec/concepts/storage/partial_notes.md @@ -142,15 +142,16 @@ Those `G_x` are generators that generated [here](https://github.com/AztecProtoco We can see the complete implementation of creating and completing partial notes in an Aztec contract in the `setup_refund` and `complete_refund` functions. -#### `setup_refund` +#### `fee_entrypoint_private` -#include_code setup_refund noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust +#include_code fee_entrypoint_private noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr rust -The `setup_refund` function sets the `complete_refund` function to be called at the end of the public function execution (`set_public_teardown_function`). This ensures that the partial notes will be completed and the fee payer will be paid and the user refund will be issued. +The `fee_entrypoint_private` function sets the `complete_refund` function to be called at the end of the public function execution (`set_public_teardown_function`). +This ensures that the refund partial note will be completed for the user. #### `complete_refund` -#include_code complete_refund noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust +#include_code complete_refund noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr rust ## Future work diff --git a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md index ba59aae84e2c..9c58a4bb1602 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md @@ -312,12 +312,6 @@ After initializing storage, the function checks that the `msg_sender` is authori #include_code burn_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -#### `setup_refund` - -This private function may be called by a Fee Paying Contract (FPC) in order to allow users to pay transaction fees privately on the network. This function ensures that the user has enough funds in their account to pay the transaction fees for the transaction, sets up partial notes for paying the fees to the `fee_payer` and sending any unspent fees back to the user, and enqueues a call to the internal, public [`complete_refund`](#complete_refund) function to be run as part of the public execution step. - -#include_code setup_refund /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - #### `prepare_private_balance_increase` TODO: update from `prepare_transfer_to_private` @@ -342,12 +336,6 @@ This function is called from [`burn`](#burn). The account's private balance is d #include_code reduce_total_supply /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -#### `complete_refund` - -This public function is intended to be called during the public teardown at the end of public transaction execution. The call to this function is staged in [`setup_refund`](#setup_refund). This function ensures that the user has sufficient funds to cover the transaction costs and emits encrypted notes to the fee payer and the remaining, unused transaction fee back to the user. - -#include_code complete_refund /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - #### `_finalize_transfer_to_private_unsafe` This public internal function decrements the public balance of the `from` account and finalizes the partial note for the recipient, which is hidden in the `hiding_point_slot`. diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 3b1df32eb30f..6dd587c70614 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -73,6 +73,7 @@ contract FPC { /// - the `max_fee`, /// - which FPC has been used to make the payment, /// - the asset which was used to make the payment. + // docs:start:fee_entrypoint_private #[private] fn fee_entrypoint_private(max_fee: U128, nonce: Field) { // TODO(PR #8022): Once PublicImmutable performs only 1 merkle proof here, we'll save ~4k gates @@ -100,9 +101,11 @@ contract FPC { // Set the FPC as the fee payer of the tx. context.set_as_fee_payer(); } + // docs:end:fee_entrypoint_private /// Executed as a public teardown function and is responsible for completing the refund in a private fee payment /// flow. + // docs:start:complete_refund #[public] #[internal] fn complete_refund(accepted_asset: AztecAddress, refund_slot: Field, max_fee: U128) { @@ -120,6 +123,7 @@ contract FPC { &mut context, ); } + // docs:end:complete_refund /// Pays for the tx fee with msg_sender's public balance of accepted asset (AA). The maximum fee a user is willing /// to pay is defined by `max_fee` and is denominated in AA. From 6e4759ac79251ccbea0e2e7ecdb904f97b555e1b Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 30 Jan 2025 20:35:02 +0000 Subject: [PATCH 4/6] docs fixes --- .../contracts/fpc_contract/src/main.nr | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 6dd587c70614..8b1e43bd527b 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -103,7 +103,7 @@ contract FPC { } // docs:end:fee_entrypoint_private - /// Executed as a public teardown function and is responsible for completing the refund in a private fee payment + /// Executed as a public teardown function and is responsible for completing the refund in the private fee payment /// flow. // docs:start:complete_refund #[public] @@ -111,11 +111,11 @@ contract FPC { fn complete_refund(accepted_asset: AztecAddress, refund_slot: Field, max_fee: U128) { let tx_fee = U128::from_integer(context.transaction_fee()); - // 1. We check that user funded the fee payer contract with at least the transaction fee. - // TODO(#7796): we should try to prevent reverts here + // 1. Check that user funded the fee payer contract with at least the transaction fee. + // TODO(#10805): we should try to prevent reverts here assert(max_fee >= tx_fee, "max fee not enough to cover tx fee"); - // 2. We compute the refund amount as the difference between funded amount and the tx fee. + // 2. Compute the refund amount as the difference between funded amount and the tx fee. // TODO(#10805): Introduce a real exchange rate let refund_amount = max_fee - tx_fee; @@ -175,9 +175,9 @@ contract FPC { ); } - /// Pays the refund to the `refund_recipient`. The refund is the difference between the `max_fee` and - /// the actual fee. `accepted_asset` is the asset in which the refund is paid. It's passed as an argument - /// to avoid the need for another read from public storage. + /// Pays the refund to the `refund_recipient` as part of the public fee payment flow. The refund is the difference + /// between the `max_fee` and the actual fee. `accepted_asset` is the asset in which the refund is paid. + /// It's passed as an argument to avoid the need for another read from public storage. #[public] #[internal] fn pay_refund(refund_recipient: AztecAddress, max_fee: U128, accepted_asset: AztecAddress) { From 8c51dee725e1c01568a3e9f03b4ce791e0d50e83 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 30 Jan 2025 20:47:41 +0000 Subject: [PATCH 5/6] better docs --- .../noir-contracts/contracts/fpc_contract/src/main.nr | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 8b1e43bd527b..4047cfc41f96 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -82,6 +82,9 @@ contract FPC { let user = context.msg_sender(); let token = Token::at(accepted_asset); + // TODO(#10805): Here we should check that `max_fee` converted to fee juice is enough to cover the tx + // fee juice/mana/gas limit. Currently the fee juice/AA exchange rate is fixed 1:1. + // Pull the max fee from the user's balance of the accepted asset to the public balance of this contract. token.transfer_to_public(user, context.this_address(), max_fee, nonce).call(&mut context); @@ -112,7 +115,7 @@ contract FPC { let tx_fee = U128::from_integer(context.transaction_fee()); // 1. Check that user funded the fee payer contract with at least the transaction fee. - // TODO(#10805): we should try to prevent reverts here + // TODO(#10805): Nuke this check once we have a proper max_fee check in the fee_entrypoint_private. assert(max_fee >= tx_fee, "max fee not enough to cover tx fee"); // 2. Compute the refund amount as the difference between funded amount and the tx fee. From a925a87a830fe4815f93aaa7a85cc2dee4c6a732 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 31 Jan 2025 17:01:28 +0000 Subject: [PATCH 6/6] migration notes + fmt --- docs/docs/migration_notes.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index 9ac4b7e756c3..4a3ae90bf69a 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -8,7 +8,14 @@ Aztec is in full-speed development. Literally every version breaks compatibility ## TBD +### [Token, FPC] Moving fee-related complexity from the Token to the FPC + +There was a complexity leak of fee-related functionality in the token contract. +We've came up with a way how to achieve the same objective with the general functionality of the Token contract. +This lead to the removal of `setup_refund` and `complete_refund` functions from the Token contract and addition of `complete_refund` function to the FPC. + ### [Aztec.nr] Improved storage slot allocation + State variables are no longer assumed to be generic over a type that implements the `Serialize` trait: instead, they must implement the `Storage` trait with an `N` value equal to the number of slots they need to reserve. For the vast majority of state variables, this simply means binding the serialization length to this trait: @@ -18,6 +25,7 @@ For the vast majority of state variables, this simply means binding the serializ ``` ### [Aztec.nr] Introduction of `Packable` trait + We have introduced a `Packable` trait that allows types to be serialized and deserialized with a focus on minimizing the size of the resulting Field array. This is in contrast to the `Serialize` and `Deserialize` traits, which follows Noir's intrinsic serialization format. This is a breaking change because we now require `Packable` trait implementation for any type that is to be stored in contract storage. @@ -43,6 +51,7 @@ impl Packable for U128 { ### Logs for notes, partial notes, and events have been refactored. We're preparing to make log assembly more customisable. These paths have changed. + ```diff - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note, + use dep::aztec::encrypted_logs::log_assembly_strategies::default_aes128::note::encode_and_encrypt_note, @@ -57,6 +66,7 @@ The way in which logs are assembled in this "default_aes128" strategy is has als You can remove this method from any custom notes or events that you've implemented. ### [Aztec.nr] Packing notes resulting in changes in `NoteInterface` + Note interface implementation generated by our macros now packs note content instead of serializing it With this change notes are being less costly DA-wise to emit when some of the note struct members implements the `Packable` trait (this is typically the `UintNote` which represents `value` as `U128` that gets serialized as 2 fields but packed as 1). This results in the following changes in the `NoteInterface`: @@ -97,7 +107,9 @@ have been merged into getContractMetadata These functions have been merged into `pxe.getContractMetadata` and `pxe.getContractClassMetadata`. ## 0.72.0 + ### Some functions in `aztec.js` and `@aztec/accounts` are now async + In our efforts to make libraries more browser-friendly and providing with more bundling options for `bb.js` (like a non top-level-await version), some functions are being made async, in particular those that access our cryptographic functions. ```diff @@ -109,7 +121,9 @@ In our efforts to make libraries more browser-friendly and providing with more b ``` ### Public logs replace unencrypted logs + Any log emitted from public is now known as a public log, rather than an unencrypted log. This means methods relating to these logs have been renamed e.g. in the pxe, archiver, txe: + ```diff - getUnencryptedLogs(filter: LogFilter): Promise - getUnencryptedEvents(eventMetadata: EventMetadataDefinition, from: number, limit: number): Promise @@ -118,47 +132,58 @@ Any log emitted from public is now known as a public log, rather than an unencry ``` The context method in aztec.nr is now: + ```diff - context.emit_unencrypted_log(log) + context.emit_public_log(log) ``` These logs were treated as bytes in the node and as hashes in the protocol circuits. Now, public logs are treated as fields everywhere: + ```diff - unencryptedLogs: UnencryptedTxL2Logs - unencrypted_logs_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX] + publicLogs: PublicLog[] + public_logs: [PublicLog; MAX_PUBLIC_LOGS_PER_TX] ``` + A `PublicLog` contains the log (as an array of fields) and the app address. This PR also renamed encrypted events to private events: + ```diff - getEncryptedEvents(eventMetadata: EventMetadataDefinition, from: number, limit: number, vpks: Point[]): Promise + getPrivateEvents(eventMetadata: EventMetadataDefinition, from: number, limit: number, vpks: Point[]): Promise ``` ## 0.70.0 + ### [Aztec.nr] Removal of `getSiblingPath` oracle + Use `getMembershipWitness` oracle instead that returns both the sibling path and index. ## 0.68.0 + ### [archiver, node, pxe] Remove contract artifacts in node and archiver and store function names instead + Contract artifacts were only in the archiver for debugging purposes. Instead function names are now (optionally) emitted when registering contract classes Function changes in the Node interface and Contract Data source interface: + ```diff - addContractArtifact(address: AztecAddress, artifact: ContractArtifact): Promise; + registerContractFunctionNames(address: AztecAddress, names: Record): Promise; ``` So now the PXE registers this when calling `registerContract()` + ``` await this.node.registerContractFunctionNames(instance.address, functionNames); ``` Function changes in the Archiver + ```diff - addContractArtifact(address: AztecAddress, artifact: ContractArtifact) - getContractArtifact(address: AztecAddress) @@ -166,6 +191,7 @@ Function changes in the Archiver ``` ### [fees, fpc] Changes in setting up FPC as fee payer on AztecJS and method names in FPC + On AztecJS, setting up `PrivateFeePaymentMethod` and `PublicFeePaymentMethod` are now the same. The don't need to specify a sequencer address or which coin to pay in. The coins are set up in the FPC contract! ```diff @@ -177,6 +203,7 @@ On AztecJS, setting up `PrivateFeePaymentMethod` and `PublicFeePaymentMethod` ar ``` Changes in `FeePaymentMethod` class in AztecJS + ```diff - getAsset(): AztecAddress; + getAsset(): Promise; @@ -192,6 +219,7 @@ Also created a public function `pull_funds()` for admin to clawback any money in Expect more changes in FPC in the coming releases! ### Name change from `contact` to `sender` in PXE API + `contact` has been deemed confusing because the name is too similar to `contract`. For this reason we've decided to rename it: