diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index a2be4ac607a..9579b1cd46d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -183,6 +183,10 @@ impl PrivateContext { } } + pub fn set_as_fee_payer(&mut self) { + self.is_fee_payer = true; + } + pub fn end_setup(&mut self) { self.min_revertible_side_effect_counter = self.side_effect_counter; } diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr index 03cd9da4ba7..f51d506af83 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr @@ -43,6 +43,7 @@ contract AppSubscription { note.remaining_txs -= 1; storage.subscriptions.at(user_address).replace(&mut note, true); + context.set_as_fee_payer(); let gas_limit = storage.gas_token_limit_per_tx.read_private(); context.set_public_teardown_function( storage.gas_token_address.read_private(), 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 878dd0c84a3..d6c808c17f1 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -22,6 +22,7 @@ contract FPC { fn fee_entrypoint_private(amount: Field, asset: AztecAddress, secret_hash: Field, nonce: Field) { assert(asset == storage.other_asset.read_private()); Token::at(asset).unshield(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); + context.set_as_fee_payer(); // Would like to get back to // FPC::at(context.this_address()).pay_fee_with_shielded_rebate(amount, asset, secret_hash).set_public_teardown_function(&mut context); context.set_public_teardown_function( @@ -34,6 +35,7 @@ contract FPC { #[aztec(private)] fn fee_entrypoint_public(amount: Field, asset: AztecAddress, nonce: Field) { FPC::at(context.this_address()).prepare_fee(context.msg_sender(), amount, asset, nonce).enqueue(&mut context); + context.set_as_fee_payer(); // TODO(#6277) for improving interface: // FPC::at(context.this_address()).pay_fee(context.msg_sender(), amount, asset).set_public_teardown_function(&mut context); context.set_public_teardown_function( 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 9028b64478f..619b30fd2b9 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -197,6 +197,11 @@ contract Test { ); } + #[aztec(private)] + fn test_setting_fee_payer() { + context.set_as_fee_payer(); + } + // Purely exists for testing #[aztec(public)] fn create_l2_to_l1_message_public(amount: Field, secret_hash: Field, portal_address: EthAddress) { diff --git a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts index 53702e7048c..5d7f61b36c5 100644 --- a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts @@ -208,6 +208,7 @@ describe('e2e_fees dapp_subscription', () => { const action = counterContract.methods.increment(bobAddress).request(); const txExReq = await dappEntrypoint.createTxExecutionRequest({ calls: [action] }); const tx = await pxe.proveTx(txExReq, true); + expect(tx.data.feePayer).toEqual(subscriptionContract.address); const sentTx = new SentTx(pxe, pxe.sendTx(tx)); return sentTx.wait(); } diff --git a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts index ec8b0909724..572b4947ee7 100644 --- a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts @@ -103,15 +103,17 @@ describe('e2e_fees private_payment', () => { * this is expected to squash notes and nullifiers */ const transferAmount = 5n; - const tx = await bananaCoin.methods - .transfer(aliceAddress, bobAddress, transferAmount, 0n) - .send({ - fee: { - gasSettings, - paymentMethod: new PrivateFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet, refundSecret), - }, - }) - .wait(); + const interaction = bananaCoin.methods.transfer(aliceAddress, bobAddress, transferAmount, 0n); + + const localTx = await interaction.prove({ + fee: { + gasSettings, + paymentMethod: new PrivateFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet, refundSecret), + }, + }); + expect(localTx.data.feePayer).toEqual(bananaFPC.address); + + const tx = await interaction.send().wait(); /** * at present the user is paying DA gas for: diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index b012ad3de30..502ccaad546 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -865,6 +865,23 @@ describe('Private Execution test suite', () => { }); }); + describe('setting fee payer', () => { + it('should default to not being a fee payer', async () => { + // arbitrary random function that doesn't set a fee payer + const entrypoint = getFunctionArtifact(TestContractArtifact, 'emit_msg_sender'); + const contractAddress = AztecAddress.random(); + const result = await runSimulator({ artifact: entrypoint, contractAddress }); + expect(result.callStackItem.publicInputs.isFeePayer).toBe(false); + }); + + it('should be able to set a fee payer', async () => { + const entrypoint = getFunctionArtifact(TestContractArtifact, 'test_setting_fee_payer'); + const contractAddress = AztecAddress.random(); + const result = await runSimulator({ artifact: entrypoint, contractAddress }); + expect(result.callStackItem.publicInputs.isFeePayer).toBe(true); + }); + }); + describe('pending note hashes contract', () => { beforeEach(() => { oracle.getFunctionArtifact.mockImplementation((_, selector) =>