From bd6368bd16159aad88906496cb9d6270e483a26e Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 7 Sep 2023 08:09:56 -0300 Subject: [PATCH] chore(contracts): Use autogenerated Noir interfaces where possible (#2073) Fixes #1604 --- .../examples/uniswap_trade_on_l1_from_l2.ts | 2 - .../src/uniswap_trade_on_l1_from_l2.test.ts | 2 - .../end-to-end/src/e2e_multi_transfer.test.ts | 48 +--- .../src/uniswap_trade_on_l1_from_l2.test.ts | 2 - .../multi_transfer_contract/src/main.nr | 72 +++-- .../src/private_token_airdrop_interface.nr | 1 + .../native_token_contract/src/main.nr | 8 +- .../src/interface.nr | 248 ++++++++++++++++++ .../contracts/uniswap_contract/src/main.nr | 13 +- .../src/non_native_token_interface.nr | 1 + .../noir-contracts/src/scripts/copy_output.ts | 2 +- yarn-project/noir-libs/noir-aztec/src/auth.nr | 8 + yarn-project/noir-libs/noir-aztec/src/lib.nr | 1 + 13 files changed, 317 insertions(+), 91 deletions(-) create mode 120000 yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/private_token_airdrop_interface.nr create mode 100644 yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/interface.nr create mode 120000 yarn-project/noir-contracts/src/contracts/uniswap_contract/src/non_native_token_interface.nr create mode 100644 yarn-project/noir-libs/noir-aztec/src/auth.nr diff --git a/yarn-project/aztec-sandbox/src/examples/uniswap_trade_on_l1_from_l2.ts b/yarn-project/aztec-sandbox/src/examples/uniswap_trade_on_l1_from_l2.ts index 09bcbd2028c..62cefe4ed74 100644 --- a/yarn-project/aztec-sandbox/src/examples/uniswap_trade_on_l1_from_l2.ts +++ b/yarn-project/aztec-sandbox/src/examples/uniswap_trade_on_l1_from_l2.ts @@ -234,12 +234,10 @@ async function main() { // 4. Send L2 to L1 message to withdraw funds and another message to swap assets. logger('Send L2 tx to withdraw WETH to uniswap portal and send message to swap assets on L1'); // recipient is the uniswap portal - const selector = wethL2Contract.methods.withdraw.selector.toField(); const minimumOutputAmount = 0n; const withdrawTx = uniswapL2Contract.methods .swap( - selector, wethL2Contract.address.toField(), wethAmountToBridge, new Fr(3000), diff --git a/yarn-project/canary/src/uniswap_trade_on_l1_from_l2.test.ts b/yarn-project/canary/src/uniswap_trade_on_l1_from_l2.test.ts index c9b2691158e..7f630728e37 100644 --- a/yarn-project/canary/src/uniswap_trade_on_l1_from_l2.test.ts +++ b/yarn-project/canary/src/uniswap_trade_on_l1_from_l2.test.ts @@ -262,12 +262,10 @@ describe('uniswap_trade_on_l1_from_l2', () => { // 4. Send L2 to L1 message to withdraw funds and another message to swap assets. logger('Send L2 tx to withdraw WETH to uniswap portal and send message to swap assets on L1'); // recipient is the uniswap portal - const selector = wethL2Contract.methods.withdraw.selector.toField(); const minimumOutputAmount = 0n; const withdrawTx = uniswapL2Contract.methods .swap( - selector, wethL2Contract.address.toField(), wethAmountToBridge, new Fr(3000), diff --git a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts index ed48e142d0d..ca5422e2058 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -100,12 +100,11 @@ describe('multi-transfer payments', () => { it('12 transfers per transactions should work', async () => { // Transaction 1 logger(`self batchTransfer()`); - const batchTransferTx = zkTokenContract.methods + await zkTokenContract.methods .batchTransfer(ownerAddress, [200n, 300n, 400n], [ownerAddress, ownerAddress, ownerAddress], 0) - .send({ origin: ownerAddress }); - await batchTransferTx.isMined(); - const batchTransferTxReceipt = await batchTransferTx.getReceipt(); - logger(`consumption Receipt status: ${batchTransferTxReceipt.status}`); + .send({ origin: ownerAddress }) + .wait(); + await expectBalance(zkTokenContract, ownerAddress, initialBalance); await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 4); @@ -115,19 +114,10 @@ describe('multi-transfer payments', () => { // Transaction 2 logger(`multiTransfer()...`); - const multiTransferTx = multiTransferContract.methods - .multiTransfer( - zkTokenContract.address.toField(), - recipients, - amounts, - ownerAddress, - zkTokenContract.methods.batchTransfer.selector.toField(), - noteOffsets, - ) - .send({ origin: ownerAddress }); - await multiTransferTx.isMined({ timeout: 1000 }); // mining timeout ≥ time needed for the test to finish. - const multiTransferTxReceipt = await multiTransferTx.getReceipt(); - logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); + await multiTransferContract.methods + .multiTransfer(zkTokenContract.address.toField(), recipients, amounts, ownerAddress, noteOffsets) + .send({ origin: ownerAddress }) + .wait({ timeout: 1000 }); // mining timeout ≥ time needed for the test to finish. await expectBalance(zkTokenContract, ownerAddress, initialBalance - amountSum); for (let index = 0; index < numberOfAccounts; index++) { @@ -178,19 +168,10 @@ describe('multi-transfer payments', () => { const noteOffsets: bigint[] = [0n, 0n, 3n, 6n]; const repeatedSelfAdddress: AztecAddress[] = Array(12).fill(ownerAddress); - const multiTransferTx = multiTransferContract.methods - .multiTransfer( - zkTokenContract.address.toField(), - repeatedSelfAdddress, - amounts, - ownerAddress, - zkTokenContract.methods.batchTransfer.selector.toField(), - noteOffsets, - ) - .send({ origin: ownerAddress }); - await multiTransferTx.isMined({ timeout: 100 }); // mining timeout ≥ time needed for the test to finish. - const multiTransferTxReceipt = await multiTransferTx.getReceipt(); - logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); + await multiTransferContract.methods + .multiTransfer(zkTokenContract.address.toField(), repeatedSelfAdddress, amounts, ownerAddress, noteOffsets) + .send({ origin: ownerAddress }) + .wait({ timeout: 100 }); // mining timeout ≥ time needed for the test to finish. await expectBalance(zkTokenContract, ownerAddress, initialBalance); await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); @@ -203,10 +184,7 @@ describe('multi-transfer payments', () => { const recipient = recipients[0]; await expectBalance(zkTokenContract, recipient, 0n); - const transferTx = zkTokenContract.methods.transfer(transferAmount, recipient).send({ origin: ownerAddress }); - await transferTx.isMined(); - const txReceipt = await transferTx.getReceipt(); - logger(`consumption Receipt status: ${txReceipt.status}`); + await zkTokenContract.methods.transfer(transferAmount, recipient).send({ origin: ownerAddress }).wait(); await expectBalance(zkTokenContract, ownerAddress, initialBalance - transferAmount); await expectBalance(zkTokenContract, recipient, transferAmount); diff --git a/yarn-project/end-to-end/src/uniswap_trade_on_l1_from_l2.test.ts b/yarn-project/end-to-end/src/uniswap_trade_on_l1_from_l2.test.ts index 688815a24d2..b92a05134f6 100644 --- a/yarn-project/end-to-end/src/uniswap_trade_on_l1_from_l2.test.ts +++ b/yarn-project/end-to-end/src/uniswap_trade_on_l1_from_l2.test.ts @@ -189,12 +189,10 @@ describe('uniswap_trade_on_l1_from_l2', () => { // 4. Send L2 to L1 message to withdraw funds and another message to swap assets. logger('Send L2 tx to withdraw WETH to uniswap portal and send message to swap assets on L1'); - const selector = wethCrossChainHarness.l2Contract.methods.withdraw.selector.toField(); const minimumOutputAmount = 0; const withdrawTx = uniswapL2Contract.methods .swap( - selector, wethCrossChainHarness.l2Contract.address.toField(), wethAmountToBridge, new Fr(3000), diff --git a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr index 2e9bf850ddb..102bd6383ce 100644 --- a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr @@ -1,3 +1,5 @@ +mod private_token_airdrop_interface; + // Demonstrates how to perform 4 x 4 = 16 transfers in one transaction. Uses the private airdrop contract in the backend. contract MultiTransfer { use dep::aztec::oracle::public_call; @@ -14,6 +16,9 @@ contract MultiTransfer { utils as note_utils, }; + // Interfaces + use crate::private_token_airdrop_interface::PrivateTokenAirdropPrivateContextInterface; + #[aztec(private)] fn constructor() {} @@ -28,60 +33,45 @@ contract MultiTransfer { addresses: [Field; 12], // Addresses to distribute to amounts: [Field; 12], // Amounts to distribute owner: Field, // Owner of the asset - batch_transfer_selector: Field, // Function selector for transfer note_offsets: [Field; 4], // Offsets from which 4 notes of the owner would be read. ) -> [Field; 4] { + let token = PrivateTokenAirdropPrivateContextInterface::at(asset); + // First batch transfer call - let return_values_1 = context.call_private_function(asset, batch_transfer_selector, [ + let result1 = token.batchTransfer( + &mut context, owner, - amounts[0], - amounts[1], - amounts[2], - addresses[0], - addresses[1], - addresses[2], - note_offsets[0], - ]); - let result1 = return_values_1[0]; + [amounts[0], amounts[1], amounts[2]], + [addresses[0], addresses[1], addresses[2]], + note_offsets[0] as u32, + )[0]; // Second batch transfer call - let return_values_2 = context.call_private_function(asset, batch_transfer_selector, [ + let result2 = token.batchTransfer( + &mut context, owner, - amounts[3], - amounts[4], - amounts[5], - addresses[3], - addresses[4], - addresses[5], - note_offsets[1], - ]); - let result2 = return_values_2[0]; + [amounts[3], amounts[4], amounts[5]], + [addresses[3], addresses[4], addresses[5]], + note_offsets[1] as u32, + )[0]; // Third batch transfer call - let return_values_3 = context.call_private_function(asset, batch_transfer_selector, [ + let result3 = token.batchTransfer( + &mut context, owner, - amounts[6], - amounts[7], - amounts[8], - addresses[6], - addresses[7], - addresses[8], - note_offsets[2], - ]); - let result3 = return_values_3[0]; + [amounts[6], amounts[7], amounts[8]], + [addresses[6], addresses[7], addresses[8]], + note_offsets[2] as u32, + )[0]; // Fourth batch transfer call - let return_values_4 = context.call_private_function(asset, batch_transfer_selector, [ + let result4 = token.batchTransfer( + &mut context, owner, - amounts[9], - amounts[10], - amounts[11], - addresses[9], - addresses[10], - addresses[11], - note_offsets[3], - ]); - let result4 = return_values_4[0]; + [amounts[9], amounts[10], amounts[11]], + [addresses[9], addresses[10], addresses[11]], + note_offsets[3] as u32, + )[0]; [result1, result2, result3, result4] } diff --git a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/private_token_airdrop_interface.nr b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/private_token_airdrop_interface.nr new file mode 120000 index 00000000000..3fe452c3079 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/private_token_airdrop_interface.nr @@ -0,0 +1 @@ +../../private_token_airdrop_contract/src/interface.nr \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr index 27c8979b3fa..02500944991 100644 --- a/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr @@ -36,7 +36,8 @@ contract NativeToken { compute_selector::compute_selector }, public_call_stack_item::PublicCallStackItem, - types::point::Point + types::point::Point, + auth::assert_valid_message_for, }; #[aztec(private)] @@ -321,9 +322,8 @@ contract NativeToken { ], GENERATOR_INDEX__SIGNATURE_PAYLOAD )[0]; - let is_valid_selector = compute_selector("is_valid(Field)"); - let _callStackItem0 = context.call_private_function(from, is_valid_selector, [message_field]); - assert(_callStackItem0[0] == is_valid_selector); + + assert_valid_message_for(&mut context, from, message_field); } // Reduce user balance diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/interface.nr b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/interface.nr new file mode 100644 index 00000000000..21be69b3a1f --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/interface.nr @@ -0,0 +1,248 @@ +/* Autogenerated file, do not edit! */ + +use dep::std; +use dep::aztec::context::{ PrivateContext, PublicContext }; +use dep::aztec::constants_gen::RETURN_VALUES_LENGTH; + + + +// Interface for calling NonNativeToken functions from a private context +struct NonNativeTokenPrivateContextInterface { + address: Field, +} + +impl NonNativeTokenPrivateContextInterface { + fn at(address: Field) -> Self { + Self { + address, + } + } + + fn addUnshieldedBalance( + self, + context: &mut PrivateContext, + amount: Field, + recipient: Field + ) { + let mut serialised_args = [0; 2]; + serialised_args[0] = amount; + serialised_args[1] = recipient; + + context.call_public_function(self.address, 0x716a727f, serialised_args) + } + + + fn mint( + self, + context: &mut PrivateContext, + amount: Field, + owner: Field, + msg_key: Field, + secret: Field, + canceller: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 5]; + serialised_args[0] = amount; + serialised_args[1] = owner; + serialised_args[2] = msg_key; + serialised_args[3] = secret; + serialised_args[4] = canceller; + + context.call_private_function(self.address, 0x641662d1, serialised_args) + } + + + fn mintPublic( + self, + context: &mut PrivateContext, + amount: Field, + owner_address: Field, + msg_key: Field, + secret: Field, + canceller: Field + ) { + let mut serialised_args = [0; 5]; + serialised_args[0] = amount; + serialised_args[1] = owner_address; + serialised_args[2] = msg_key; + serialised_args[3] = secret; + serialised_args[4] = canceller; + + context.call_public_function(self.address, 0x93343cc1, serialised_args) + } + + + fn redeemShield( + self, + context: &mut PrivateContext, + amount: Field, + secret: Field, + owner: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 3]; + serialised_args[0] = amount; + serialised_args[1] = secret; + serialised_args[2] = owner; + + context.call_private_function(self.address, 0x8ecff612, serialised_args) + } + + + fn shield( + self, + context: &mut PrivateContext, + amount: Field, + secretHash: Field + ) { + let mut serialised_args = [0; 2]; + serialised_args[0] = amount; + serialised_args[1] = secretHash; + + context.call_public_function(self.address, 0x72451161, serialised_args) + } + + + fn transfer( + self, + context: &mut PrivateContext, + amount: Field, + recipient: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 2]; + serialised_args[0] = amount; + serialised_args[1] = recipient; + + context.call_private_function(self.address, 0xc0888d22, serialised_args) + } + + + fn unshieldTokens( + self, + context: &mut PrivateContext, + amount: Field, + recipient: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 2]; + serialised_args[0] = amount; + serialised_args[1] = recipient; + + context.call_private_function(self.address, 0x906b1f4f, serialised_args) + } + + + fn withdraw( + self, + context: &mut PrivateContext, + amount: Field, + sender: Field, + recipient: Field, + callerOnL1: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 4]; + serialised_args[0] = amount; + serialised_args[1] = sender; + serialised_args[2] = recipient; + serialised_args[3] = callerOnL1; + + context.call_private_function(self.address, 0x760d58ea, serialised_args) + } + + + fn withdrawPublic( + self, + context: &mut PrivateContext, + amount: Field, + recipient: Field, + callerOnL1: Field + ) { + let mut serialised_args = [0; 3]; + serialised_args[0] = amount; + serialised_args[1] = recipient; + serialised_args[2] = callerOnL1; + + context.call_public_function(self.address, 0xc80c80d3, serialised_args) + } + +} + + + + +// Interface for calling NonNativeToken functions from a public context +struct NonNativeTokenPublicContextInterface { + address: Field, +} + +impl NonNativeTokenPublicContextInterface { + fn at(address: Field) -> Self { + Self { + address, + } + } + + fn addUnshieldedBalance( + self, + context: PublicContext, + amount: Field, + recipient: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 2]; + serialised_args[0] = amount; + serialised_args[1] = recipient; + + context.call_public_function(self.address, 0x716a727f, serialised_args) + } + + + fn mintPublic( + self, + context: PublicContext, + amount: Field, + owner_address: Field, + msg_key: Field, + secret: Field, + canceller: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 5]; + serialised_args[0] = amount; + serialised_args[1] = owner_address; + serialised_args[2] = msg_key; + serialised_args[3] = secret; + serialised_args[4] = canceller; + + context.call_public_function(self.address, 0x93343cc1, serialised_args) + } + + + fn shield( + self, + context: PublicContext, + amount: Field, + secretHash: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 2]; + serialised_args[0] = amount; + serialised_args[1] = secretHash; + + context.call_public_function(self.address, 0x72451161, serialised_args) + } + + + fn withdrawPublic( + self, + context: PublicContext, + amount: Field, + recipient: Field, + callerOnL1: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 3]; + serialised_args[0] = amount; + serialised_args[1] = recipient; + serialised_args[2] = callerOnL1; + + context.call_public_function(self.address, 0xc80c80d3, serialised_args) + } + +} + + diff --git a/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/main.nr index 13ab9d48489..b41f6c4ea3b 100644 --- a/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/main.nr @@ -1,3 +1,5 @@ +mod non_native_token_interface; + // Demonstrates how to send a message to a portal contract on L1. We use Uniswap here as it's the most typical example. contract Uniswap { use dep::aztec::oracle::{ @@ -8,6 +10,8 @@ contract Uniswap { use dep::aztec::public_call_stack_item::PublicCallStackItem; use dep::aztec::types::point::Point; + use crate::non_native_token_interface::NonNativeTokenPrivateContextInterface; + #[aztec(private)] fn constructor() {} @@ -21,7 +25,6 @@ contract Uniswap { #[aztec(private)] fn swap( - withdrawFnSelector: Field, // withdraw method on inputAsset (l2 contract) that would withdraw to L1 inputAsset: Field, inputAmount: Field, uniswapFeeTier: Field, // which uniswap tier to use (eg 3000 for 0.3% fee) @@ -42,12 +45,14 @@ contract Uniswap { // inputAsset.withdraw(inputAmount, sender, recipient=l1UniswapPortal, callerOnL1=l1UniswapPortal) // only uniswap portal can call this (done to safeguard ordering of message consumption) // ref: https://docs.aztec.network/aztec/how-it-works/l1-l2-messaging#designated-caller - let return_values = context.call_private_function(inputAsset, withdrawFnSelector, [ + let inputAssetInterface = NonNativeTokenPrivateContextInterface::at(inputAsset); + let return_value = inputAssetInterface.withdraw( + &mut context, inputAmount, sender, l1UniswapPortal, l1UniswapPortal, - ]); + )[0]; // Send the swap message to L1 portal let content_hash = _compute_swap_content_hash( @@ -64,7 +69,7 @@ contract Uniswap { ); context.message_portal(content_hash); - return_values[0] + return_value } // refer `l1-contracts/test/portals/UniswapPortal.sol` on how L2 to L1 message is expected diff --git a/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/non_native_token_interface.nr b/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/non_native_token_interface.nr new file mode 120000 index 00000000000..52c95853c13 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/non_native_token_interface.nr @@ -0,0 +1 @@ +../../non_native_token_contract/src/interface.nr \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/scripts/copy_output.ts b/yarn-project/noir-contracts/src/scripts/copy_output.ts index 69846b59908..1b6150fe58b 100644 --- a/yarn-project/noir-contracts/src/scripts/copy_output.ts +++ b/yarn-project/noir-contracts/src/scripts/copy_output.ts @@ -23,7 +23,7 @@ const PROJECT_CONTRACTS = [ { name: 'SchnorrAuthWitnessAccount', target: '../aztec.js/src/abis/', exclude: [] }, ]; -const INTERFACE_CONTRACTS = ['private_token', 'private_token_airdrop', 'test']; +const INTERFACE_CONTRACTS = ['private_token', 'private_token_airdrop', 'non_native_token', 'test']; /** * Writes the contract to a specific project folder, if needed. diff --git a/yarn-project/noir-libs/noir-aztec/src/auth.nr b/yarn-project/noir-libs/noir-aztec/src/auth.nr new file mode 100644 index 00000000000..8f3de21820b --- /dev/null +++ b/yarn-project/noir-libs/noir-aztec/src/auth.nr @@ -0,0 +1,8 @@ +use crate::context::PrivateContext; + +global IS_VALID_SELECTOR = 0xe86ab4ff; + +fn assert_valid_message_for(context: &mut PrivateContext, whom: Field, message_hash: Field) { + let result = context.call_private_function(whom, IS_VALID_SELECTOR, [message_hash])[0]; + assert(result == IS_VALID_SELECTOR, "Message not authorised by account"); +} \ No newline at end of file diff --git a/yarn-project/noir-libs/noir-aztec/src/lib.nr b/yarn-project/noir-libs/noir-aztec/src/lib.nr index 1bd4f1efeb4..81197356698 100644 --- a/yarn-project/noir-libs/noir-aztec/src/lib.nr +++ b/yarn-project/noir-libs/noir-aztec/src/lib.nr @@ -1,4 +1,5 @@ mod abi; +mod auth; mod context; mod log; mod messaging;