diff --git a/docs/docs/dev_docs/contracts/portals/main.md b/docs/docs/dev_docs/contracts/portals/main.md index 649ef0dc0a7..a7b7340f17c 100644 --- a/docs/docs/dev_docs/contracts/portals/main.md +++ b/docs/docs/dev_docs/contracts/portals/main.md @@ -50,7 +50,7 @@ Computing the `content` must be done manually in its current form, as we are sti :::info The `content_hash` is a sha256 truncated to a field element (~ 254 bits). In Aztec-nr, you can use our `sha256_to_field()` to do a sha256 hash which fits in one field element: -#include_code mint_public_content_hash_nr /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/util.nr rust +#include_code mint_public_content_hash_nr /yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/src/lib.nr rust In solidity, you can use our `Hash.sha256ToField()` method: diff --git a/l1-contracts/test/portals/TokenPortal.sol b/l1-contracts/test/portals/TokenPortal.sol index d03f2cc74bf..19ef29bb3a5 100644 --- a/l1-contracts/test/portals/TokenPortal.sol +++ b/l1-contracts/test/portals/TokenPortal.sol @@ -27,16 +27,16 @@ contract TokenPortal { // docs:start:deposit_public /** * @notice Deposit funds into the portal and adds an L2 message which can only be consumed publicly on Aztec - * @param _amount - The amount to deposit * @param _to - The aztec address of the recipient + * @param _amount - The amount to deposit * @param _canceller - The address that can cancel the L1 to L2 message * @param _deadline - The timestamp after which the entry can be cancelled * @param _secretHash - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element) * @return The key of the entry in the Inbox */ function depositToAztecPublic( - uint256 _amount, bytes32 _to, + uint256 _amount, address _canceller, uint32 _deadline, bytes32 _secretHash @@ -47,7 +47,7 @@ contract TokenPortal { // Hash the message content to be reconstructed in the receiving contract bytes32 contentHash = Hash.sha256ToField( - abi.encodeWithSignature("mint_public(uint256,bytes32,address)", _amount, _to, _canceller) + abi.encodeWithSignature("mint_public(bytes32,uint256,address)", _to, _amount, _canceller) ); // Hold the tokens in the portal @@ -60,16 +60,16 @@ contract TokenPortal { /** * @notice Deposit funds into the portal and adds an L2 message which can only be consumed privately on Aztec - * @param _amount - The amount to deposit * @param _secretHashForRedeemingMintedNotes - The hash of the secret to redeem minted notes privately on Aztec. The hash should be 254 bits (so it can fit in a Field element) + * @param _amount - The amount to deposit * @param _canceller - The address that can cancel the L1 to L2 message * @param _deadline - The timestamp after which the entry can be cancelled * @param _secretHashForL2MessageConsumption - The hash of the secret consumable L1 to L2 message. The hash should be 254 bits (so it can fit in a Field element) * @return The key of the entry in the Inbox */ function depositToAztecPrivate( - uint256 _amount, bytes32 _secretHashForRedeemingMintedNotes, + uint256 _amount, address _canceller, uint32 _deadline, bytes32 _secretHashForL2MessageConsumption @@ -81,9 +81,9 @@ contract TokenPortal { // Hash the message content to be reconstructed in the receiving contract bytes32 contentHash = Hash.sha256ToField( abi.encodeWithSignature( - "mint_private(uint256,bytes32,address)", - _amount, + "mint_private(bytes32,uint256,address)", _secretHashForRedeemingMintedNotes, + _amount, _canceller ) ); @@ -101,16 +101,16 @@ contract TokenPortal { /** * @notice Cancel a public depositToAztec L1 to L2 message * @dev only callable by the `canceller` of the message - * @param _amount - The amount to deposit per the original message * @param _to - The aztec address of the recipient in the original message + * @param _amount - The amount to deposit per the original message * @param _deadline - The timestamp after which the entry can be cancelled * @param _secretHash - The hash of the secret consumable message in the original message * @param _fee - The fee paid to the sequencer * @return The key of the entry in the Inbox */ function cancelL1ToAztecMessagePublic( - uint256 _amount, bytes32 _to, + uint256 _amount, uint32 _deadline, bytes32 _secretHash, uint64 _fee @@ -122,7 +122,7 @@ contract TokenPortal { sender: l1Actor, recipient: l2Actor, content: Hash.sha256ToField( - abi.encodeWithSignature("mint_public(uint256,bytes32,address)", _amount, _to, msg.sender) + abi.encodeWithSignature("mint_public(bytes32,uint256,address)", _to, _amount, msg.sender) ), secretHash: _secretHash, deadline: _deadline, @@ -138,16 +138,16 @@ contract TokenPortal { /** * @notice Cancel a private depositToAztec L1 to L2 message * @dev only callable by the `canceller` of the message - * @param _amount - The amount to deposit per the original message * @param _secretHashForRedeemingMintedNotes - The hash of the secret to redeem minted notes privately on Aztec + * @param _amount - The amount to deposit per the original message * @param _deadline - The timestamp after which the entry can be cancelled * @param _secretHashForL2MessageConsumption - The hash of the secret consumable L1 to L2 message * @param _fee - The fee paid to the sequencer * @return The key of the entry in the Inbox */ function cancelL1ToAztecMessagePrivate( - uint256 _amount, bytes32 _secretHashForRedeemingMintedNotes, + uint256 _amount, uint32 _deadline, bytes32 _secretHashForL2MessageConsumption, uint64 _fee @@ -160,9 +160,9 @@ contract TokenPortal { recipient: l2Actor, content: Hash.sha256ToField( abi.encodeWithSignature( - "mint_private(uint256,bytes32,address)", - _amount, + "mint_private(bytes32,uint256,address)", _secretHashForRedeemingMintedNotes, + _amount, msg.sender ) ), @@ -183,13 +183,13 @@ contract TokenPortal { /** * @notice Withdraw funds from the portal * @dev Second part of withdraw, must be initiated from L2 first as it will consume a message from outbox - * @param _amount - The amount to withdraw * @param _recipient - The address to send the funds to + * @param _amount - The amount to withdraw * @param _withCaller - Flag to use `msg.sender` as caller, otherwise address(0) * Must match the caller of the message (specified from L2) to consume it. * @return The key of the entry in the Outbox */ - function withdraw(uint256 _amount, address _recipient, bool _withCaller) + function withdraw(address _recipient, uint256 _amount, bool _withCaller) external returns (bytes32) { @@ -198,9 +198,9 @@ contract TokenPortal { recipient: DataStructures.L1Actor(address(this), block.chainid), content: Hash.sha256ToField( abi.encodeWithSignature( - "withdraw(uint256,address,address)", - _amount, + "withdraw(address,uint256,address)", _recipient, + _amount, _withCaller ? msg.sender : address(0) ) ) diff --git a/l1-contracts/test/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index 53340684b87..aeb110fb830 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -86,9 +86,9 @@ contract TokenPortalTest is Test { recipient: DataStructures.L2Actor(l2TokenAddress, 1), content: Hash.sha256ToField( abi.encodeWithSignature( - "mint_private(uint256,bytes32,address)", - amount, + "mint_private(bytes32,uint256,address)", secretHashForRedeemingMintedNotes, + amount, _canceller ) ), @@ -107,7 +107,7 @@ contract TokenPortalTest is Test { sender: DataStructures.L1Actor(address(tokenPortal), block.chainid), recipient: DataStructures.L2Actor(l2TokenAddress, 1), content: Hash.sha256ToField( - abi.encodeWithSignature("mint_public(uint256,bytes32,address)", amount, to, _canceller) + abi.encodeWithSignature("mint_public(bytes32,uint256,address)", to, amount, _canceller) ), secretHash: secretHashForL2MessageConsumption, deadline: deadline, @@ -142,8 +142,8 @@ contract TokenPortalTest is Test { // Perform op bytes32 entryKey = tokenPortal.depositToAztecPrivate{value: bid}( - amount, secretHashForRedeemingMintedNotes, + amount, address(this), deadline, secretHashForL2MessageConsumption @@ -185,7 +185,7 @@ contract TokenPortalTest is Test { // Perform op bytes32 entryKey = tokenPortal.depositToAztecPublic{value: bid}( - amount, to, address(this), deadline, secretHashForL2MessageConsumption + to, amount, address(this), deadline, secretHashForL2MessageConsumption ); assertEq(entryKey, expectedEntryKey, "returned entry key and calculated entryKey should match"); @@ -210,7 +210,7 @@ contract TokenPortalTest is Test { abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey) ); tokenPortal.cancelL1ToAztecMessagePublic( - amount, to, deadline, secretHashForL2MessageConsumption, bid + to, amount, deadline, secretHashForL2MessageConsumption, bid ); vm.stopPrank(); @@ -221,7 +221,7 @@ contract TokenPortalTest is Test { abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey) ); tokenPortal.cancelL1ToAztecMessagePrivate( - amount, secretHashForRedeemingMintedNotes, deadline, secretHashForL2MessageConsumption, bid + secretHashForRedeemingMintedNotes, amount, deadline, secretHashForL2MessageConsumption, bid ); // actually cancel the message @@ -231,7 +231,7 @@ contract TokenPortalTest is Test { emit L1ToL2MessageCancelled(expectedEntryKey); // perform op bytes32 entryKey = tokenPortal.cancelL1ToAztecMessagePublic( - amount, to, deadline, secretHashForL2MessageConsumption, bid + to, amount, deadline, secretHashForL2MessageConsumption, bid ); assertEq(entryKey, expectedEntryKey, "returned entry key and calculated entryKey should match"); @@ -257,7 +257,7 @@ contract TokenPortalTest is Test { abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey) ); tokenPortal.cancelL1ToAztecMessagePrivate( - amount, secretHashForRedeemingMintedNotes, deadline, secretHashForL2MessageConsumption, bid + secretHashForRedeemingMintedNotes, amount, deadline, secretHashForL2MessageConsumption, bid ); vm.stopPrank(); @@ -268,7 +268,7 @@ contract TokenPortalTest is Test { abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey) ); tokenPortal.cancelL1ToAztecMessagePublic( - amount, to, deadline, secretHashForL2MessageConsumption, bid + to, amount, deadline, secretHashForL2MessageConsumption, bid ); // actually cancel the message @@ -278,7 +278,7 @@ contract TokenPortalTest is Test { emit L1ToL2MessageCancelled(expectedEntryKey); // perform op bytes32 entryKey = tokenPortal.cancelL1ToAztecMessagePrivate( - amount, secretHashForRedeemingMintedNotes, deadline, secretHashForL2MessageConsumption, bid + secretHashForRedeemingMintedNotes, amount, deadline, secretHashForL2MessageConsumption, bid ); assertEq(entryKey, expectedEntryKey, "returned entry key and calculated entryKey should match"); @@ -302,7 +302,7 @@ contract TokenPortalTest is Test { recipient: DataStructures.L1Actor({actor: address(tokenPortal), chainId: block.chainid}), content: Hash.sha256ToField( abi.encodeWithSignature( - "withdraw(uint256,address,address)", withdrawAmount, recipient, _designatedCaller + "withdraw(address,uint256,address)", recipient, withdrawAmount, _designatedCaller ) ) }) @@ -331,7 +331,7 @@ contract TokenPortalTest is Test { vm.startPrank(_caller); vm.expectEmit(true, true, true, true); emit MessageConsumed(expectedEntryKey, address(tokenPortal)); - bytes32 actualEntryKey = tokenPortal.withdraw(withdrawAmount, recipient, false); + bytes32 actualEntryKey = tokenPortal.withdraw(recipient, withdrawAmount, false); assertEq(expectedEntryKey, actualEntryKey); // Should have received 654 RNA tokens assertEq(portalERC20.balanceOf(recipient), withdrawAmount); @@ -340,7 +340,7 @@ contract TokenPortalTest is Test { vm.expectRevert( abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, actualEntryKey) ); - tokenPortal.withdraw(withdrawAmount, recipient, false); + tokenPortal.withdraw(recipient, withdrawAmount, false); vm.stopPrank(); } @@ -354,13 +354,13 @@ contract TokenPortalTest is Test { vm.expectRevert( abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, entryKeyPortalChecksAgainst) ); - tokenPortal.withdraw(withdrawAmount, recipient, true); + tokenPortal.withdraw(recipient, withdrawAmount, true); entryKeyPortalChecksAgainst = _createWithdrawMessageForOutbox(address(0)); vm.expectRevert( abi.encodeWithSelector(Errors.Outbox__NothingToConsume.selector, entryKeyPortalChecksAgainst) ); - tokenPortal.withdraw(withdrawAmount, recipient, false); + tokenPortal.withdraw(recipient, withdrawAmount, false); vm.stopPrank(); } @@ -370,7 +370,7 @@ contract TokenPortalTest is Test { vm.expectEmit(true, true, true, true); emit MessageConsumed(expectedEntryKey, address(tokenPortal)); - bytes32 actualEntryKey = tokenPortal.withdraw(withdrawAmount, recipient, true); + bytes32 actualEntryKey = tokenPortal.withdraw(recipient, withdrawAmount, true); assertEq(expectedEntryKey, actualEntryKey); // Should have received 654 RNA tokens assertEq(portalERC20.balanceOf(recipient), withdrawAmount); diff --git a/l1-contracts/test/portals/UniswapPortal.sol b/l1-contracts/test/portals/UniswapPortal.sol index cca4ad7eea9..41a29c39469 100644 --- a/l1-contracts/test/portals/UniswapPortal.sol +++ b/l1-contracts/test/portals/UniswapPortal.sol @@ -70,7 +70,7 @@ contract UniswapPortal { vars.outputAsset = TokenPortal(_outputTokenPortal).underlying(); // Withdraw the input asset from the portal - TokenPortal(_inputTokenPortal).withdraw(_inAmount, address(this), true); + TokenPortal(_inputTokenPortal).withdraw(address(this), _inAmount, true); { // prevent stack too deep errors vars.contentHash = Hash.sha256ToField( @@ -123,7 +123,7 @@ contract UniswapPortal { // Deposit the output asset to the L2 via its portal return TokenPortal(_outputTokenPortal).depositToAztecPublic{value: msg.value}( - amountOut, _aztecRecipient, _canceller, _deadlineForL1ToL2Message, _secretHashForL1ToL2Message + _aztecRecipient, amountOut, _canceller, _deadlineForL1ToL2Message, _secretHashForL1ToL2Message ); } // docs:end:solidity_uniswap_swap @@ -163,7 +163,7 @@ contract UniswapPortal { vars.outputAsset = TokenPortal(_outputTokenPortal).underlying(); // Withdraw the input asset from the portal - TokenPortal(_inputTokenPortal).withdraw(_inAmount, address(this), true); + TokenPortal(_inputTokenPortal).withdraw(address(this), _inAmount, true); { // prevent stack too deep errors vars.contentHash = Hash.sha256ToField( @@ -216,8 +216,8 @@ contract UniswapPortal { // Deposit the output asset to the L2 via its portal return TokenPortal(_outputTokenPortal).depositToAztecPrivate{value: msg.value}( - amountOut, _secretHashForRedeemingMintedNotes, + amountOut, _canceller, _deadlineForL1ToL2Message, _secretHashForL1ToL2Message diff --git a/l1-contracts/test/portals/UniswapPortal.t.sol b/l1-contracts/test/portals/UniswapPortal.t.sol index 9e42a3c0b8d..0e769074408 100644 --- a/l1-contracts/test/portals/UniswapPortal.t.sol +++ b/l1-contracts/test/portals/UniswapPortal.t.sol @@ -82,7 +82,7 @@ contract UniswapPortalTest is Test { sender: DataStructures.L2Actor(l2TokenAddress, 1), recipient: DataStructures.L1Actor(address(daiTokenPortal), block.chainid), content: Hash.sha256ToField( - abi.encodeWithSignature("withdraw(uint256,address,address)", amount, _recipient, _caller) + abi.encodeWithSignature("withdraw(address,uint256,address)", _recipient, amount, _caller) ) }); entryKey = outbox.computeEntryKey(message); @@ -379,7 +379,7 @@ contract UniswapPortalTest is Test { // perform op // TODO(2167) - Update UniswapPortal properly with new portal standard. bytes32 entryKey = wethTokenPortal.cancelL1ToAztecMessagePublic( - wethAmountOut, aztecRecipient, deadlineForL1ToL2Message, secretHash, 1 ether + aztecRecipient, wethAmountOut, deadlineForL1ToL2Message, secretHash, 1 ether ); assertEq(entryKey, l1ToL2MessageKey, "returned entry key and calculated entryKey should match"); assertFalse(inbox.contains(entryKey), "entry still in inbox"); diff --git a/yarn-project/acir-simulator/package.json b/yarn-project/acir-simulator/package.json index dab0530657f..fffee9b0428 100644 --- a/yarn-project/acir-simulator/package.json +++ b/yarn-project/acir-simulator/package.json @@ -52,7 +52,8 @@ "toml": "^3.0.0", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", - "typescript": "^5.0.4" + "typescript": "^5.0.4", + "viem": "^1.2.5" }, "files": [ "dest", diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index ae918a69879..58b7c5d49ae 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -35,12 +35,12 @@ import { AppendOnlyTree, Pedersen, StandardTree, newTree } from '@aztec/merkle-t import { ChildContractArtifact, ImportTestContractArtifact, - NonNativeTokenContractArtifact, ParentContractArtifact, PendingCommitmentsContractArtifact, PrivateTokenAirdropContractArtifact, StatefulTestContractArtifact, TestContractArtifact, + TokenContractArtifact, } from '@aztec/noir-contracts/artifacts'; import { PackedArguments, TxExecutionRequest } from '@aztec/types'; @@ -48,6 +48,7 @@ import { jest } from '@jest/globals'; import { MockProxy, mock } from 'jest-mock-extended'; import { default as levelup } from 'levelup'; import { type MemDown, default as memdown } from 'memdown'; +import { getFunctionSelector } from 'viem'; import { buildL1ToL2Message, getFunctionArtifact } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; @@ -649,16 +650,16 @@ describe('Private Execution test suite', () => { it('Should be able to consume a dummy cross chain message', async () => { const bridgedAmount = 100n; - const artifact = getFunctionArtifact(NonNativeTokenContractArtifact, 'mint'); + const artifact = getFunctionArtifact(TestContractArtifact, 'consume_mint_private_message'); - const secret = new Fr(1n); + const secretForL1ToL2MessageConsumption = new Fr(1n); + const secretHashForRedeemingNotes = new Fr(2n); const canceller = EthAddress.random(); - // Function selector: 0xeeb73071 keccak256('mint(uint256,bytes32,address)') const preimage = await buildL1ToL2Message( - 'eeb73071', - [new Fr(bridgedAmount), recipient.toField(), canceller.toField()], + getFunctionSelector('mint_private(bytes32,uint256,address)').substring(2), + [secretHashForRedeemingNotes, new Fr(bridgedAmount), canceller.toField()], contractAddress, - secret, + secretForL1ToL2MessageConsumption, ); // stub message key @@ -673,7 +674,13 @@ describe('Private Execution test suite', () => { }); }); - const args = [bridgedAmount, recipient, messageKey, secret, canceller.toField()]; + const args = [ + secretHashForRedeemingNotes, + bridgedAmount, + canceller.toField(), + messageKey, + secretForL1ToL2MessageConsumption, + ]; const result = await runSimulator({ contractAddress, artifact, args }); // Check a nullifier has been inserted @@ -683,14 +690,14 @@ describe('Private Execution test suite', () => { it('Should be able to consume a dummy public to private message', async () => { const amount = 100n; - const artifact = getFunctionArtifact(NonNativeTokenContractArtifact, 'redeemShield'); + const artifact = getFunctionArtifact(TokenContractArtifact, 'redeem_shield'); const wasm = await CircuitsWasm.get(); const secret = new Fr(1n); const secretHash = computeSecretMessageHash(wasm, secret); const preimage = [toBufferBE(amount, 32), secretHash.toBuffer()]; const noteHash = Fr.fromBuffer(hash(preimage)); - const storageSlot = new Fr(2); + const storageSlot = new Fr(5); const innerNoteHash = Fr.fromBuffer(hash([storageSlot.toBuffer(), noteHash.toBuffer()])); const siloedNoteHash = siloCommitment(wasm, contractAddress, innerNoteHash); oracle.getNotes.mockResolvedValue([ @@ -707,7 +714,7 @@ describe('Private Execution test suite', () => { const result = await runSimulator({ artifact, - args: [amount, secret, recipient], + args: [recipient, amount, secret], }); // Check a nullifier has been inserted. diff --git a/yarn-project/acir-simulator/src/public/index.test.ts b/yarn-project/acir-simulator/src/public/index.test.ts index c8bb5c90924..bda1757eccc 100644 --- a/yarn-project/acir-simulator/src/public/index.test.ts +++ b/yarn-project/acir-simulator/src/public/index.test.ts @@ -15,14 +15,15 @@ import { Fr } from '@aztec/foundation/fields'; import { toBigInt } from '@aztec/foundation/serialize'; import { ChildContractArtifact, - NonNativeTokenContractArtifact, ParentContractArtifact, PublicTokenContractArtifact, TestContractArtifact, + TokenContractArtifact, } from '@aztec/noir-contracts/artifacts'; import { MockProxy, mock } from 'jest-mock-extended'; import { type MemDown, default as memdown } from 'memdown'; +import { getFunctionSelector } from 'viem'; import { buildL1ToL2Message } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; @@ -285,11 +286,14 @@ describe('ACIR public execution simulator', () => { }); it('Should be able to create a commitment from the public context', async () => { - const shieldArtifact = NonNativeTokenContractArtifact.functions.find(f => f.name === 'shield')!; - const args = encodeArguments(shieldArtifact, params); + const shieldArtifact = TokenContractArtifact.functions.find(f => f.name === 'shield')!; + const msgSender = AztecAddress.random(); + const secretHash = Fr.random(); + + const args = encodeArguments(shieldArtifact, [msgSender.toField(), amount, secretHash, Fr.ZERO]); const callContext = CallContext.from({ - msgSender: AztecAddress.random(), + msgSender: msgSender, storageContractAddress: contractAddress, portalContractAddress: EthAddress.random(), functionSelector: FunctionSelector.empty(), @@ -308,11 +312,8 @@ describe('ACIR public execution simulator', () => { // Assert the commitment was created expect(result.newCommitments.length).toEqual(1); - const expectedNoteHash = pedersenPlookupCommitInputs( - wasm, - params.map(a => a.toBuffer()), - ); - const storageSlot = new Fr(2); // matches storage.nr + const expectedNoteHash = pedersenPlookupCommitInputs(wasm, [amount.toBuffer(), secretHash.toBuffer()]); + const storageSlot = new Fr(5); // for pending_shields const expectedInnerNoteHash = pedersenPlookupCommitInputs(wasm, [storageSlot.toBuffer(), expectedNoteHash]); expect(result.newCommitments[0].toBuffer()).toEqual(expectedInnerNoteHash); }); @@ -348,8 +349,8 @@ describe('ACIR public execution simulator', () => { expect(result.newL2ToL1Messages[0].toBuffer()).toEqual(expectedNewMessageValue); }); - it('Should be able to consume an Ll to L2 message in the public context', async () => { - const mintPublicArtifact = NonNativeTokenContractArtifact.functions.find(f => f.name === 'mintPublic')!; + it('Should be able to consume an L1 to L2 message in the public context', async () => { + const mintPublicArtifact = TestContractArtifact.functions.find(f => f.name === 'consume_mint_public_message')!; // Set up cross chain message const canceller = EthAddress.random(); @@ -358,10 +359,9 @@ describe('ACIR public execution simulator', () => { const secret = new Fr(1n); const recipient = AztecAddress.random(); - // Function selector: 0xeeb73071 keccak256('mint(uint256,bytes32,address)') const preimage = await buildL1ToL2Message( - 'eeb73071', - [new Fr(bridgedAmount), recipient.toField(), canceller.toField()], + getFunctionSelector('mint_public(bytes32,uint256,address)').substring(2), + [recipient.toField(), new Fr(bridgedAmount), canceller.toField()], contractAddress, secret, ); @@ -369,11 +369,11 @@ describe('ACIR public execution simulator', () => { // Stub message key const messageKey = Fr.random(); const args = encodeArguments(mintPublicArtifact, [ - bridgedAmount, recipient.toField(), + bridgedAmount, + canceller.toField(), messageKey, secret, - canceller.toField(), ]); const callContext = CallContext.from({ diff --git a/yarn-project/aztec-sandbox/src/examples/util.ts b/yarn-project/aztec-sandbox/src/examples/util.ts index 581e16514fd..a008e15dbd0 100644 --- a/yarn-project/aztec-sandbox/src/examples/util.ts +++ b/yarn-project/aztec-sandbox/src/examples/util.ts @@ -1,63 +1,7 @@ -import { AztecAddress, EthAddress, Wallet } from '@aztec/aztec.js'; -import { PortalERC20Abi, PortalERC20Bytecode, TokenPortalAbi, TokenPortalBytecode } from '@aztec/l1-artifacts'; -import { NonNativeTokenContract } from '@aztec/noir-contracts/types'; +import { EthAddress } from '@aztec/aztec.js'; import type { Abi, Narrow } from 'abitype'; -import { Account, Chain, Hex, HttpTransport, PublicClient, WalletClient, getContract } from 'viem'; - -/** - * Deploy L1 token and portal, initialize portal, deploy a non native l2 token contract and attach is to the portal. - * @param wallet - A wallet instance. - * @param walletClient - A viem WalletClient. - * @param publicClient - A viem PublicClient. - * @param rollupRegistryAddress - address of rollup registry to pass to initialize the token portal - * @param initialBalance - initial balance of the owner of the L2 contract - * @param owner - owner of the L2 contract - * @param underlyingERC20Address - address of the underlying ERC20 contract to use (if none supplied, it deploys one) - * @returns l2 contract instance, token portal instance, token portal address and the underlying ERC20 instance - */ -export async function deployAndInitializeNonNativeL2TokenContracts( - wallet: Wallet, - walletClient: WalletClient, - publicClient: PublicClient, - rollupRegistryAddress: EthAddress, - initialBalance = 0n, - owner = AztecAddress.ZERO, - underlyingERC20Address?: EthAddress, -) { - // deploy underlying contract if no address supplied - if (!underlyingERC20Address) { - underlyingERC20Address = await deployL1Contract(walletClient, publicClient, PortalERC20Abi, PortalERC20Bytecode); - } - const underlyingERC20: any = getContract({ - address: underlyingERC20Address.toString(), - abi: PortalERC20Abi, - walletClient, - publicClient, - }); - - // deploy the token portal - const tokenPortalAddress = await deployL1Contract(walletClient, publicClient, TokenPortalAbi, TokenPortalBytecode); - const tokenPortal: any = getContract({ - address: tokenPortalAddress.toString(), - abi: TokenPortalAbi, - walletClient, - publicClient, - }); - - // deploy l2 contract and attach to portal - const l2Contract = await NonNativeTokenContract.deploy(wallet, initialBalance, owner) - .send({ portalContract: tokenPortalAddress }) - .deployed(); - const l2TokenAddress = l2Contract.address.toString() as `0x${string}`; - - // initialize portal - await tokenPortal.write.initialize( - [rollupRegistryAddress.toString(), underlyingERC20Address.toString(), l2TokenAddress], - {} as any, - ); - return { l2Contract, tokenPortalAddress, tokenPortal, underlyingERC20 }; -} +import { Account, Chain, Hex, HttpTransport, PublicClient, WalletClient } from 'viem'; /** * Helper function to deploy ETH contracts. diff --git a/yarn-project/end-to-end/src/canary/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/canary/uniswap_l1_l2.ts index 09b896ea640..6ff3289d3c8 100644 --- a/yarn-project/end-to-end/src/canary/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/canary/uniswap_l1_l2.ts @@ -151,9 +151,9 @@ export const uniswapL1L2TestSuite = ( const [secretForRedeemingWeth, secretHashForRedeemingWeth] = await wethCrossChainHarness.generateClaimSecret(); const messageKey = await wethCrossChainHarness.sendTokensToPortalPrivate( + secretHashForRedeemingWeth, wethAmountToBridge, secretHashForMintingWeth, - secretHashForRedeemingWeth, ); // funds transferred from owner to token portal expect(await wethCrossChainHarness.getL1BalanceOf(ownerEthAddress)).toBe( @@ -172,8 +172,8 @@ export const uniswapL1L2TestSuite = ( // 2. Claim WETH on L2 logger('Minting weth on L2'); await wethCrossChainHarness.consumeMessageOnAztecAndMintSecretly( - wethAmountToBridge, secretHashForRedeemingWeth, + wethAmountToBridge, messageKey, secretForMintingWeth, ); @@ -271,8 +271,8 @@ export const uniswapL1L2TestSuite = ( // 6. claim dai on L2 logger('Consuming messages to mint dai on L2'); await daiCrossChainHarness.consumeMessageOnAztecAndMintSecretly( - daiAmountToBridge, secretHashForRedeemingDai, + daiAmountToBridge, depositDaiMessageKey, secretForDepositingSwappedDai, ); diff --git a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts index 1b95d120272..e862b00cc1b 100644 --- a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts +++ b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts @@ -104,7 +104,6 @@ EscrowContractArtifact ImportTestContractArtifact LendingContractArtifact MultiTransferContractArtifact -NonNativeTokenContractArtifact ParentContractArtifact PendingCommitmentsContractArtifact PokeableTokenContractArtifact diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts index 55a2e988ef0..35e152271e6 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts @@ -64,9 +64,9 @@ describe('e2e_cross_chain_messaging', () => { // 2. Deposit tokens to the TokenPortal const messageKey = await crossChainTestHarness.sendTokensToPortalPrivate( + secretHashForRedeemingMintedNotes, bridgeAmount, secretHashForL2MessageConsumption, - secretHashForRedeemingMintedNotes, ); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); @@ -80,8 +80,8 @@ describe('e2e_cross_chain_messaging', () => { // 3. Consume L1-> L2 message and mint private tokens on L2 await crossChainTestHarness.consumeMessageOnAztecAndMintSecretly( - bridgeAmount, secretHashForRedeemingMintedNotes, + bridgeAmount, messageKey, secretForL2MessageConsumption, ); @@ -125,9 +125,9 @@ describe('e2e_cross_chain_messaging', () => { await crossChainTestHarness.mintTokensOnL1(l1TokenBalance); const messageKey = await crossChainTestHarness.sendTokensToPortalPrivate( + secretHashForRedeemingMintedNotes, bridgeAmount, secretHashForL2MessageConsumption, - secretHashForRedeemingMintedNotes, ); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); @@ -146,8 +146,8 @@ describe('e2e_cross_chain_messaging', () => { l2Bridge .withWallet(user2Wallet) .methods.claim_private( - bridgeAmount, secretHashForL2MessageConsumption, + bridgeAmount, ethAccount, messageKey, secretForL2MessageConsumption, @@ -159,8 +159,8 @@ describe('e2e_cross_chain_messaging', () => { const consumptionTx = l2Bridge .withWallet(user2Wallet) .methods.claim_private( - bridgeAmount, secretHashForRedeemingMintedNotes, + bridgeAmount, ethAccount, messageKey, secretForL2MessageConsumption, @@ -193,7 +193,7 @@ describe('e2e_cross_chain_messaging', () => { await expect( l2Bridge .withWallet(user1Wallet) - .methods.exit_to_l1_private(ethAccount, l2Token.address, withdrawAmount, EthAddress.ZERO, nonce) + .methods.exit_to_l1_private(l2Token.address, ethAccount, withdrawAmount, EthAddress.ZERO, nonce) .simulate(), ).rejects.toThrowError(`Unknown auth witness for message hash 0x${expectedBurnMessageHash.toString('hex')}`); }); @@ -208,9 +208,9 @@ describe('e2e_cross_chain_messaging', () => { await crossChainTestHarness.generateClaimSecret(); const messageKey = await crossChainTestHarness.sendTokensToPortalPrivate( + Fr.random(), bridgeAmount, secretHashForL2MessageConsumption, - Fr.random(), ); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(0n); diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts index 329dc5b026d..1135d44ba7c 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts @@ -177,7 +177,7 @@ describe('e2e_public_cross_chain_messaging', () => { await expect( l2Bridge .withWallet(user2Wallet) - .methods.claim_private(bridgeAmount, secretHash, ownerEthAddress, messageKey, secret) + .methods.claim_private(secretHash, bridgeAmount, ownerEthAddress, messageKey, secret) .simulate(), ).rejects.toThrowError("Cannot satisfy constraint 'l1_to_l2_message_data.message.content == content"); }); diff --git a/yarn-project/end-to-end/src/fixtures/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/fixtures/cross_chain_test_harness.ts index 6aba183bda5..66c61f6aa68 100644 --- a/yarn-project/end-to-end/src/fixtures/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/fixtures/cross_chain_test_harness.ts @@ -6,7 +6,7 @@ import { OutboxAbi } from '@aztec/l1-artifacts'; import { TokenBridgeContract, TokenContract } from '@aztec/noir-contracts/types'; import { NotePreimage, PXE, TxStatus } from '@aztec/types'; -import { Chain, HttpTransport, PublicClient, getContract } from 'viem'; +import { Chain, HttpTransport, PublicClient, getContract, getFunctionSelector } from 'viem'; import { deployAndInitializeTokenAndBridgeContracts } from './utils.js'; @@ -119,8 +119,8 @@ export class CrossChainTestHarness { this.logger('Sending messages to L1 portal to be consumed publicly'); const args = [ - bridgeAmount, this.ownerAddress.toString(), + bridgeAmount, this.ethAccount.toString(), deadline, secretHash.toString(true), @@ -134,9 +134,9 @@ export class CrossChainTestHarness { } async sendTokensToPortalPrivate( + secretHashForRedeemingMintedNotes: Fr, bridgeAmount: bigint, secretHashForL2MessageConsumption: Fr, - secretHashForRedeemingMintedNotes: Fr, ) { await this.underlyingERC20.write.approve([this.tokenPortalAddress.toString(), bridgeAmount], {} as any); @@ -145,8 +145,8 @@ export class CrossChainTestHarness { this.logger('Sending messages to L1 portal to be consumed privately'); const args = [ - bridgeAmount, secretHashForRedeemingMintedNotes.toString(true), + bridgeAmount, this.ethAccount.toString(), deadline, secretHashForL2MessageConsumption.toString(true), @@ -183,8 +183,8 @@ export class CrossChainTestHarness { } async consumeMessageOnAztecAndMintSecretly( - bridgeAmount: bigint, secretHashForRedeemingMintedNotes: Fr, + bridgeAmount: bigint, messageKey: Fr, secretForL2MessageConsumption: Fr, ) { @@ -192,8 +192,8 @@ export class CrossChainTestHarness { // Call the mint tokens function on the Aztec.nr contract const consumptionTx = this.l2Bridge.methods .claim_private( - bridgeAmount, secretHashForRedeemingMintedNotes, + bridgeAmount, this.ethAccount, messageKey, secretForL2MessageConsumption, @@ -217,7 +217,7 @@ export class CrossChainTestHarness { async withdrawPrivateFromAztecToL1(withdrawAmount: bigint, nonce: Fr = Fr.ZERO) { const withdrawTx = this.l2Bridge.methods - .exit_to_l1_private(this.ethAccount, this.l2Token.address, withdrawAmount, EthAddress.ZERO, nonce) + .exit_to_l1_private(this.l2Token.address, this.ethAccount, withdrawAmount, EthAddress.ZERO, nonce) .send(); const withdrawReceipt = await withdrawTx.wait(); expect(withdrawReceipt.status).toBe(TxStatus.MINED); @@ -252,12 +252,12 @@ export class CrossChainTestHarness { async checkEntryIsNotInOutbox(withdrawAmount: bigint, callerOnL1: EthAddress = EthAddress.ZERO): Promise { this.logger('Ensure that the entry is not in outbox yet'); - // 0xb460af94, selector for "withdraw(uint256,address,address)" + const content = sha256ToField( Buffer.concat([ - Buffer.from([0xb4, 0x60, 0xaf, 0x94]), - new Fr(withdrawAmount).toBuffer(), + Buffer.from(getFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), this.ethAccount.toBuffer32(), + new Fr(withdrawAmount).toBuffer(), callerOnL1.toBuffer32(), ]), ); @@ -279,8 +279,8 @@ export class CrossChainTestHarness { this.logger('Send L1 tx to consume entry and withdraw funds'); // Call function on L1 contract to consume the message const { request: withdrawRequest, result: withdrawEntryKey } = await this.tokenPortal.simulate.withdraw([ - withdrawAmount, this.ethAccount.toString(), + withdrawAmount, false, ]); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index db7cfde63df..48c5179190b 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -38,7 +38,7 @@ import { TokenPortalAbi, TokenPortalBytecode, } from '@aztec/l1-artifacts'; -import { NonNativeTokenContract, TokenBridgeContract, TokenContract } from '@aztec/noir-contracts/types'; +import { TokenBridgeContract, TokenContract } from '@aztec/noir-contracts/types'; import { PXEService, createPXEService, getPXEServiceConfig } from '@aztec/pxe'; import { AztecNode, L2BlockL2Logs, LogType, PXE, TxStatus, createAztecNodeRpcClient } from '@aztec/types'; @@ -430,61 +430,6 @@ export async function deployAndInitializeTokenAndBridgeContracts( return { token, bridge, tokenPortalAddress, tokenPortal, underlyingERC20 }; } -/** - * Deploy L1 token and portal, initialize portal, deploy a non native l2 token contract and attach is to the portal. - * @param wallet - Aztec wallet instance. - * @param walletClient - A viem WalletClient. - * @param publicClient - A viem PublicClient. - * @param rollupRegistryAddress - address of rollup registry to pass to initialize the token portal - * @param initialBalance - initial balance of the owner of the L2 contract - * @param owner - owner of the L2 contract - * @param underlyingERC20Address - address of the underlying ERC20 contract to use (if none supplied, it deploys one) - * @returns l2 contract instance, token portal instance, token portal address and the underlying ERC20 instance - */ -// TODO (#2291) DELETE!!! -export async function deployAndInitializeNonNativeL2TokenContracts( - wallet: Wallet, - walletClient: WalletClient, - publicClient: PublicClient, - rollupRegistryAddress: EthAddress, - initialBalance = 0n, - owner = AztecAddress.ZERO, - underlyingERC20Address?: EthAddress, -) { - // deploy underlying contract if no address supplied - if (!underlyingERC20Address) { - underlyingERC20Address = await deployL1Contract(walletClient, publicClient, PortalERC20Abi, PortalERC20Bytecode); - } - const underlyingERC20: any = getContract({ - address: underlyingERC20Address.toString(), - abi: PortalERC20Abi, - walletClient, - publicClient, - }); - - // deploy the token portal - const tokenPortalAddress = await deployL1Contract(walletClient, publicClient, TokenPortalAbi, TokenPortalBytecode); - const tokenPortal: any = getContract({ - address: tokenPortalAddress.toString(), - abi: TokenPortalAbi, - walletClient, - publicClient, - }); - - // deploy l2 contract and attach to portal - const l2Contract = await NonNativeTokenContract.deploy(wallet, initialBalance, owner) - .send({ portalContract: tokenPortalAddress }) - .deployed(); - const l2TokenAddress = l2Contract.address.toString(); - - // initialize portal - await tokenPortal.write.initialize( - [rollupRegistryAddress.toString(), underlyingERC20Address.toString(), l2TokenAddress], - {} as any, - ); - return { l2Contract, tokenPortalAddress, tokenPortal, underlyingERC20 }; -} - /** * Sleep for a given number of milliseconds. * @param ms - the number of milliseconds to sleep for diff --git a/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts b/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts index f7816061288..85b72245269 100644 --- a/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts @@ -5,11 +5,11 @@ import { DeployL1Contracts } from '@aztec/ethereum'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { DebugLogger } from '@aztec/foundation/log'; -import { NonNativeTokenContract } from '@aztec/noir-contracts/types'; +import { TokenContract } from '@aztec/noir-contracts/types'; import { Chain, HttpTransport, PublicClient } from 'viem'; -import { delay, deployAndInitializeNonNativeL2TokenContracts, setNextBlockTimestamp, setup } from './fixtures/utils.js'; +import { delay, deployAndInitializeTokenAndBridgeContracts, setNextBlockTimestamp, setup } from './fixtures/utils.js'; // TODO (#2291) - Replace with token bridge standard describe('archiver integration with l1 to l2 messages', () => { @@ -19,7 +19,7 @@ describe('archiver integration with l1 to l2 messages', () => { let config: AztecNodeConfig; let teardown: () => Promise; - let l2Contract: NonNativeTokenContract; + let l2Token: TokenContract; let ethAccount: EthAddress; let tokenPortalAddress: EthAddress; @@ -27,11 +27,10 @@ describe('archiver integration with l1 to l2 messages', () => { let underlyingERC20: any; let publicClient: PublicClient; - const initialBalance = 10n; let owner: AztecAddress; let receiver: AztecAddress; - beforeEach(async () => { + beforeAll(async () => { let deployL1ContractsValues: DeployL1Contracts | undefined; let accounts: CompleteAddress[]; ({ teardown, wallet, deployL1ContractsValues, accounts, config, logger } = await setup(2)); @@ -46,35 +45,29 @@ describe('archiver integration with l1 to l2 messages', () => { // Deploy and initialize all required contracts logger('Deploying Portal, initializing and deploying l2 contract...'); - const contracts = await deployAndInitializeNonNativeL2TokenContracts( + const contracts = await deployAndInitializeTokenAndBridgeContracts( wallet, walletClient, publicClient, deployL1ContractsValues!.l1ContractAddresses.registryAddress!, - initialBalance, owner, ); - l2Contract = contracts.l2Contract; + l2Token = contracts.token; underlyingERC20 = contracts.underlyingERC20; tokenPortal = contracts.tokenPortal; tokenPortalAddress = contracts.tokenPortalAddress; - await expectBalance(owner, initialBalance); logger('Successfully deployed contracts and initialized portal'); }, 100_000); - afterEach(async () => { + afterAll(async () => { await archiver.stop(); await teardown(); - }, 30_000); - - const expectBalance = async (owner: AztecAddress, expectedBalance: bigint) => { - const balance = await l2Contract.methods.getBalance(owner).view({ from: owner }); - logger(`Account ${owner} balance: ${balance}`); - expect(balance).toBe(expectedBalance); - }; + }); it('cancelled l1 to l2 messages cannot be consumed by archiver', async () => { // create a message, then cancel it + const initialL1MintAmount = 1000000n; + const mintAmount = 1000n; // Generate a claim secret using pedersen logger("Generating a claim secret using pedersen's hash function"); @@ -84,19 +77,17 @@ describe('archiver integration with l1 to l2 messages', () => { logger('Generated claim secret: ' + secretString); logger('Minting tokens on L1'); - await underlyingERC20.write.mint([ethAccount.toString(), 1000000n], {} as any); - await underlyingERC20.write.approve([tokenPortalAddress.toString(), 1000n], {} as any); - - expect(await underlyingERC20.read.balanceOf([ethAccount.toString()])).toBe(1000000n); + await underlyingERC20.write.mint([ethAccount.toString(), initialL1MintAmount], {} as any); + expect(await underlyingERC20.read.balanceOf([ethAccount.toString()])).toBe(initialL1MintAmount); // Deposit tokens to the TokenPortal + await underlyingERC20.write.approve([tokenPortalAddress.toString(), mintAmount], {} as any); const deadline = Number((await publicClient.getBlock()).timestamp + 1000n); - const mintAmount = 100n; logger('Sending messages to L1 portal'); - const args = [mintAmount, owner.toString(), ethAccount.toString(), deadline, secretString] as const; + const args = [owner.toString(), mintAmount, ethAccount.toString(), deadline, secretString] as const; await tokenPortal.write.depositToAztecPublic(args, {} as any); - expect(await underlyingERC20.read.balanceOf([ethAccount.toString()])).toBe(1000000n - mintAmount); + expect(await underlyingERC20.read.balanceOf([ethAccount.toString()])).toBe(initialL1MintAmount - mintAmount); // Wait for the archiver to process the message await delay(5000); /// waiting 5 seconds. @@ -106,9 +97,9 @@ describe('archiver integration with l1 to l2 messages', () => { // cancel the message logger('cancelling the l1 to l2 message'); - const argsCancel = [mintAmount, owner.toString(), deadline, secretString, 0n] as const; + const argsCancel = [owner.toString(), mintAmount, deadline, secretString, 0n] as const; await tokenPortal.write.cancelL1ToAztecMessagePublic(argsCancel, { gas: 1_000_000n } as any); - expect(await underlyingERC20.read.balanceOf([ethAccount.toString()])).toBe(1000000n); + expect(await underlyingERC20.read.balanceOf([ethAccount.toString()])).toBe(initialL1MintAmount); // let archiver sync up await delay(5000); @@ -118,8 +109,7 @@ describe('archiver integration with l1 to l2 messages', () => { it('archiver handles l1 to l2 message correctly even when l2block has no such messages', async () => { // send a transfer tx to force through rollup with the message included - const transferAmount = 1n; - l2Contract.methods.transfer(transferAmount, receiver).send(); + await l2Token.methods.transfer_public(owner, receiver, 0n, 0n).send().wait(); expect((await archiver.getPendingL1ToL2Messages(10)).length).toEqual(0); expect(() => archiver.getConfirmedL1ToL2Message(Fr.ZERO)).toThrow(); diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index f1e3bc6fe80..a1df90d29c2 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -10,7 +10,6 @@ members = [ "src/contracts/import_test_contract", "src/contracts/lending_contract", "src/contracts/multi_transfer_contract", - "src/contracts/non_native_token_contract", "src/contracts/parent_contract", "src/contracts/pending_commitments_contract", "src/contracts/pokeable_token_contract", diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/Nargo.toml deleted file mode 100644 index 5e51ed65b25..00000000000 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/Nargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "non_native_token_contract" -authors = [""] -compiler_version = "0.1" -type = "contract" - -[dependencies] -aztec = { path = "../../../../aztec-nr/aztec" } -value_note = { path = "../../../../aztec-nr/value-note"} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/hash.nr b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/hash.nr deleted file mode 100644 index b0f1e56cbc3..00000000000 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/hash.nr +++ /dev/null @@ -1,78 +0,0 @@ -use dep::std::hash::sha256; - -// Computes a content hash of a deposit/mint message. -pub fn get_mint_content_hash(amount: Field, owner_address: Field, canceller: Field) -> Field { - let mut hash_bytes: [u8; 100] = [0; 100]; - let amount_bytes = amount.to_be_bytes(32); - let recipient_bytes = owner_address.to_be_bytes(32); - let canceller_bytes = canceller.to_be_bytes(32); - - for i in 0..32 { - hash_bytes[i + 4] = amount_bytes[i]; - hash_bytes[i + 36] = recipient_bytes[i]; - hash_bytes[i + 68] = canceller_bytes[i]; - } - - // Function selector: 0xeeb73071 keccak256('mint(uint256,bytes32,address)') - hash_bytes[0] = 0xee; - hash_bytes[1] = 0xb7; - hash_bytes[2] = 0x30; - hash_bytes[3] = 0x71; - - let content_sha256 = sha256(hash_bytes); - - // // Convert the content_sha256 to a field element - let mut v = 1; - let mut high = 0 as Field; - let mut low = 0 as Field; - - for i in 0..16 { - high = high + (content_sha256[15 - i] as Field) * v; - low = low + (content_sha256[16 + 15 - i] as Field) * v; - v = v * 256; - } - - // Abuse that a % p + b % p = (a + b) % p and that low < p - let content_hash = low + high * v; - content_hash -} - -// Computes a content hash of a withdraw message. -pub fn get_withdraw_content_hash(amount: Field, recipient: Field, callerOnL1: Field) -> Field { - // Compute the content hash - // Compute sha256(selector || amount || recipient) - // then convert to a single field element - // add that to the l2 to l1 messages - let mut hash_bytes: [u8; 100] = [0; 100]; - let amount_bytes = amount.to_be_bytes(32); - let recipient_bytes = recipient.to_be_bytes(32); - let callerOnL1_bytes = callerOnL1.to_be_bytes(32); - - // 0xb460af94, selector for "withdraw(uint256,address,address)" - hash_bytes[0] = 0xb4; - hash_bytes[1] = 0x60; - hash_bytes[2] = 0xaf; - hash_bytes[3] = 0x94; - - for i in 0..32 { - hash_bytes[i + 4] = amount_bytes[i]; - hash_bytes[i + 36] = recipient_bytes[i]; - hash_bytes[i + 68] = callerOnL1_bytes[i]; - } - let content_sha256 = sha256(hash_bytes); - - // Convert the content_sha256 to a field element - let mut v = 1; - let mut high = 0 as Field; - let mut low = 0 as Field; - - for i in 0..16 { - high = high + (content_sha256[15 - i] as Field) * v; - low = low + (content_sha256[16 + 15 - i] as Field) * v; - v = v * 256; - } - - // Abuse that a % p + b % p = (a + b) % p and that low < p - let content = low + high * v; - content -} \ No newline at end of file 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 deleted file mode 100644 index 880bfe0729c..00000000000 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/interface.nr +++ /dev/null @@ -1,248 +0,0 @@ -/* 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 { - pub fn at(address: Field) -> Self { - Self { - address, - } - } - - pub fn addUnshieldedBalance( - self, - context: &mut PrivateContext, - amount: Field, - recipient: Field - ) { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - - context.call_public_function(self.address, 0x716a727f, serialized_args) - } - - - pub fn mint( - self, - context: &mut PrivateContext, - amount: Field, - owner: Field, - msg_key: Field, - secret: Field, - canceller: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 5]; - serialized_args[0] = amount; - serialized_args[1] = owner; - serialized_args[2] = msg_key; - serialized_args[3] = secret; - serialized_args[4] = canceller; - - context.call_private_function(self.address, 0x641662d1, serialized_args) - } - - - pub fn mintPublic( - self, - context: &mut PrivateContext, - amount: Field, - owner_address: Field, - msg_key: Field, - secret: Field, - canceller: Field - ) { - let mut serialized_args = [0; 5]; - serialized_args[0] = amount; - serialized_args[1] = owner_address; - serialized_args[2] = msg_key; - serialized_args[3] = secret; - serialized_args[4] = canceller; - - context.call_public_function(self.address, 0x93343cc1, serialized_args) - } - - - pub fn redeemShield( - self, - context: &mut PrivateContext, - amount: Field, - secret: Field, - owner: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 3]; - serialized_args[0] = amount; - serialized_args[1] = secret; - serialized_args[2] = owner; - - context.call_private_function(self.address, 0x8ecff612, serialized_args) - } - - - pub fn shield( - self, - context: &mut PrivateContext, - amount: Field, - secretHash: Field - ) { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secretHash; - - context.call_public_function(self.address, 0x72451161, serialized_args) - } - - - pub fn transfer( - self, - context: &mut PrivateContext, - amount: Field, - recipient: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - - context.call_private_function(self.address, 0xc0888d22, serialized_args) - } - - - pub fn unshieldTokens( - self, - context: &mut PrivateContext, - amount: Field, - recipient: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - - context.call_private_function(self.address, 0x906b1f4f, serialized_args) - } - - - pub fn withdraw( - self, - context: &mut PrivateContext, - amount: Field, - sender: Field, - recipient: Field, - callerOnL1: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 4]; - serialized_args[0] = amount; - serialized_args[1] = sender; - serialized_args[2] = recipient; - serialized_args[3] = callerOnL1; - - context.call_private_function(self.address, 0x760d58ea, serialized_args) - } - - - pub fn withdrawPublic( - self, - context: &mut PrivateContext, - amount: Field, - recipient: Field, - callerOnL1: Field - ) { - let mut serialized_args = [0; 3]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - serialized_args[2] = callerOnL1; - - context.call_public_function(self.address, 0xc80c80d3, serialized_args) - } - -} - - - - -// Interface for calling NonNativeToken functions from a public context -struct NonNativeTokenPublicContextInterface { - address: Field, -} - -impl NonNativeTokenPublicContextInterface { - pub fn at(address: Field) -> Self { - Self { - address, - } - } - - pub fn addUnshieldedBalance( - self, - context: PublicContext, - amount: Field, - recipient: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - - context.call_public_function(self.address, 0x716a727f, serialized_args) - } - - - pub fn mintPublic( - self, - context: PublicContext, - amount: Field, - owner_address: Field, - msg_key: Field, - secret: Field, - canceller: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 5]; - serialized_args[0] = amount; - serialized_args[1] = owner_address; - serialized_args[2] = msg_key; - serialized_args[3] = secret; - serialized_args[4] = canceller; - - context.call_public_function(self.address, 0x93343cc1, serialized_args) - } - - - pub fn shield( - self, - context: PublicContext, - amount: Field, - secretHash: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secretHash; - - context.call_public_function(self.address, 0x72451161, serialized_args) - } - - - pub fn withdrawPublic( - self, - context: PublicContext, - amount: Field, - recipient: Field, - callerOnL1: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 3]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - serialized_args[2] = callerOnL1; - - context.call_public_function(self.address, 0xc80c80d3, serialized_args) - } - -} - - diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr deleted file mode 100644 index 8fd7c7212f7..00000000000 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr +++ /dev/null @@ -1,322 +0,0 @@ -mod hash; -mod transparent_note; - -// Represents an ERC20 token bridged from L1 to L2 via `l1-contracts/test/portals/TokenPortal.sol`. The bridged user -// balance can be stored in both public and private domain. If the balance is stored in the private domain then it -// is represented as a set of notes in the private data tree. If the balance is stored in the public domain then it is -// represented as a single value in the public data tree. -// -// It is also used to test insertion of a note from public domain into the private data tree. How this works is that -// a user provides a secret hash as an argument on the input of the public function, and the contract then inserts -// a note into the private data tree once the public call is processed. A note can later on be spent by providing -// the secret hash preimage on the input of a private function call. -// -// This flow will be typically used when performing a token swap: -// 1. A calls a private function which enqueues a public call to the DEX contract (with the secret hash on input). -// 2. The sequencer performs the public call to the DEX and the note gets inserted into the private data tree. -// 3. At this point a recipient can spend the note in a private function assuming he/she knows the secret. -contract NonNativeToken { - // Libs - use dep::std::option::Option; - use dep::value_note::{ - balance_utils, - utils::{increment, decrement}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, - }; - - use crate::transparent_note::{ - TransparentNote, - TransparentNoteMethods, - TRANSPARENT_NOTE_LEN, - }; - - use crate::hash::{get_mint_content_hash, get_withdraw_content_hash}; - - use dep::aztec::{ - context::{PrivateContext, PublicContext, Context}, - note::{ - note_getter_options::NoteGetterOptions, - note_header::NoteHeader, - utils as note_utils, - }, - selector::compute_selector, - state_vars::{map::Map, public_state::PublicState, set::Set}, - types::type_serialization::field_serialization::{ - FieldSerializationMethods, FIELD_SERIALIZED_LEN, - }, - }; - - struct Storage { - balances: Map>, - pending_shields: Set, - public_balances: Map>, - } - - impl Storage { - fn init(context: Context) -> pub Self { - Storage { - balances: Map::new( - context, - 1, // Storage slot - |context, slot| { - Set::new(context, slot, ValueNoteMethods) - }, - ), - pending_shields: Set::new(context, 2, TransparentNoteMethods), - public_balances: Map::new( - context, - 3, - |context, slot| { - PublicState::new( - context, - slot, - FieldSerializationMethods, - ) - }, - ), - } - } - } - - #[aztec(private)] - fn constructor( - initial_supply: Field, - owner: Field, - ) { - - - let balance = storage.balances.at(owner); - increment(balance, initial_supply, owner); - } - - // Mint Private Function - // This mint function differs to the typical token mint function as it only allows minting - // upon consuming valid messages from a token portal contract - // docs:start:non_native_token_mint - #[aztec(private)] - fn mint( - amount: Field, - owner: Field, - msg_key: Field, - secret: Field, - canceller: Field, - ) { - - - let content_hash = get_mint_content_hash(amount, owner, canceller); - - // Get the l1 message from an oracle call - context.consume_l1_to_l2_message(msg_key, content_hash, secret); - - let balance = storage.balances.at(owner); - increment(balance, amount, owner); - } - // docs:end:non_native_token_mint - - // Withdraws using user's private balance. - // @dev Destroys 2 of user's notes and sends a message to the L1 portal contract. That message can then be consumed - // by calling the `withdraw` function on the L1 portal contract (assuming the args are set correctly). - // docs:start:non_native_token_withdraw - #[aztec(private)] - fn withdraw( - amount: Field, - sender: Field, // TODO: Should verify sender. - recipient: Field, // ethereum address in the field - callerOnL1: Field, // ethereum address that can call this function on the L1 portal (0x0 if anyone can call) - ) { - - - let sender_balance = storage.balances.at(sender); - decrement(sender_balance, amount, sender); - - let content = get_withdraw_content_hash(amount, recipient, callerOnL1); - context.message_portal(content); - } - // docs:end:non_native_token_withdraw - - // Mint Public Function - // This mint function differs to the typical token mint function as it only allows minting - // upon consuming valid messages from a token portal contract - #[aztec(public)] - fn mintPublic( - amount: Field, - owner_address: Field, - msg_key: Field, - secret: Field, - canceller: Field, - ) -> Field { - - let public_balances = storage.public_balances; - - let content_hash = get_mint_content_hash(amount, owner_address, canceller); - - // Consume message and emit nullifier - context.consume_l1_to_l2_message(msg_key, content_hash, secret); - - // Update the public balance - let recipient_balance = public_balances.at(owner_address); - let new_balance = recipient_balance.read() + amount; - recipient_balance.write(new_balance); - - // Push the return value into the context - new_balance - } - - - // Withdraws using user's public balance. - #[aztec(public)] - fn withdrawPublic( - amount: Field, - recipient: Field, - callerOnL1: Field, // ethereum address that can call this function on the L1 portal (0x0 if anyone can call) - ) { - - let public_balances = storage.public_balances; - - let sender = context.msg_sender(); - let sender_balance = public_balances.at(sender); - - let current_sender_balance: Field = sender_balance.read(); - - if (current_sender_balance as u120) > (amount as u120) { - // User has sufficient balance so we decrement it by `amount` - let _void1 = sender_balance.write(current_sender_balance - amount); - } - // TODO: Revert if there is not enough balance - - let content = get_withdraw_content_hash(amount, recipient, callerOnL1); - - // Emit the l2 to l1 message - context.message_portal(content); - } - - - // Transfers `amount` of tokens from `sender`'s private balance to a `recipient`. - // Note: Copied from PrivateToken - #[aztec(private)] - fn transfer( - amount: Field, - recipient: Field, - ) { - - let sender = context.msg_sender(); - - // Gets the set of sender's notes and picks 2 of those. - let sender_balance = storage.balances.at(sender); - decrement(sender_balance, amount, sender); - - let balance = storage.balances.at(recipient); - increment(balance, amount, recipient); - } - - // Shield creates a way for a user to move tokens from the public context into the private context. - #[aztec(public)] - fn shield( - amount: Field, - secretHash: Field, - ) { - - let public_balances = storage.public_balances; - let pending_shields = storage.pending_shields; - - // Decrease user's balance. - let sender = context.msg_sender(); - let sender_balance = public_balances.at(sender); - let current_sender_balance: Field = sender_balance.read(); - - assert(current_sender_balance as u120 >= amount as u120); - - // User has sufficient balance so we decrement it by `amount` - let _void1 = sender_balance.write(current_sender_balance - amount); - - // Create a commitment to the "amount" using the "secretHash" - // and insert it into the set of "pending_shields" and therefore - // (eventually) the private data tree. - let mut note = TransparentNote::new(amount, secretHash); - pending_shields.insert_from_public(&mut note); - } - - // The shield function takes a public balance, and creates a commitment containing the amount of tokens - // in the private context. - #[aztec(private)] - fn redeemShield( - amount: Field, - secret: Field, - owner: Field, - ) { - let pending_shields = storage.pending_shields; - - // Find the note that has the exact amount and secret hash. - let secret_hash = TransparentNote::compute_secret_hash(secret); - let options = NoteGetterOptions::new().select(0, amount).select(1, secret_hash).set_limit(1); - let notes = pending_shields.get_notes(options); - let note = notes[0].unwrap_unchecked(); - - // Remove the note so that it can't be redeemed again. - pending_shields.remove(note); - - // Mint the tokens - let balance = storage.balances.at(owner); - increment(balance, amount, owner); - } - - #[aztec(private)] - fn unshieldTokens( - amount: Field, - recipient: Field, - ) { - - let owner = context.msg_sender(); - - // Remove user balance - let sender_balance = storage.balances.at(owner); - decrement(sender_balance, amount, owner); - - // enqueue a public function to perform the public state update. - let thisAddress = context.this_address(); - - let addUnshieldedBalance = compute_selector("addUnshieldedBalance(Field,Field)"); - let _callStackItem1 = context.call_public_function(thisAddress, addUnshieldedBalance, [amount, recipient]); - } - - #[aztec(public)] - fn addUnshieldedBalance( - amount: Field, - recipient: Field, - ) { - - - let recipient_balance = storage.public_balances.at(recipient); - let current_balance = recipient_balance.read(); - let new_balance = current_balance + amount; - recipient_balance.write(new_balance); - } - - unconstrained fn getBalance( - owner: Field, - ) -> Field { - - let owner_balance = storage.balances.at(owner); - - balance_utils::get_balance(owner_balance) - } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - if (storage_slot == 2) { - note_utils::compute_note_hash_and_nullifier(TransparentNoteMethods, note_header, preimage) - } else { - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } - } - - unconstrained fn publicBalanceOf( - owner: Field, - ) -> Field { - - storage.public_balances.at(owner).read() - } -} diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/transparent_note.nr b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/transparent_note.nr deleted file mode 100644 index a5ac0bf85a5..00000000000 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/transparent_note.nr +++ /dev/null @@ -1,127 +0,0 @@ -use dep::std::hash::pedersen; -use dep::std::hash::pedersen_with_separator; -use dep::aztec::note::{ - note_header::NoteHeader, - note_interface::NoteInterface, - utils::compute_siloed_note_hash, -}; -use dep::aztec::constants_gen::GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET; - -global TRANSPARENT_NOTE_LEN: Field = 2; - -// Transparent note represents a note that is created in the clear (public execution), -// but can only be spent by those that know the preimage of the "secret_hash" -struct TransparentNote { - amount: Field, - secret_hash: Field, - // the fields below are not serialized/deserialized - secret: Field, - header: NoteHeader, -} - -impl TransparentNote { - - // CONSTRUCTORS - - pub fn new(amount: Field, secret_hash: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: secret_hash, - secret: 0, - header: NoteHeader::empty(), - } - } - - // new oracle call primitive - // get me the secret corresponding to this hash - pub fn new_from_secret(amount: Field, secret: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: TransparentNote::compute_secret_hash(secret), - secret: secret, - header: NoteHeader::empty(), - } - } - - - // STANDARD NOTE_INTERFACE FUNCTIONS - - pub fn serialize(self) -> [Field; TRANSPARENT_NOTE_LEN] { - [self.amount, self.secret_hash] - } - - pub fn deserialize(preimage: [Field; TRANSPARENT_NOTE_LEN]) -> Self { - TransparentNote { - amount: preimage[0], - secret_hash: preimage[1], - secret: 0, - header: NoteHeader::empty(), - } - } - - pub fn compute_note_hash(self) -> Field { - // TODO(#1205) Should use a non-zero generator index. - dep::std::hash::pedersen([ - self.amount, - self.secret_hash, - ])[0] - } - - pub fn compute_nullifier(self) -> Field { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): should use - // `compute_note_hash_for_read_or_nullify` once public functions inject nonce! - let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); - // TODO(#1205) Should use a non-zero generator index. - pedersen([self.secret, siloed_note_hash])[0] - } - - pub fn set_header(&mut self, header: NoteHeader) { - self.header = header; - } - - - // CUSTOM FUNCTIONS FOR THIS NOTE TYPE - - pub fn compute_secret_hash(secret: Field) -> Field { - // TODO(#1205) This is probably not the right index to use - pedersen_with_separator([secret], GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET)[0] - } - - pub fn knows_secret(self, secret: Field) { - let hash = TransparentNote::compute_secret_hash(secret); - assert(self.secret_hash == hash); - } -} - -fn deserialize(preimage: [Field; TRANSPARENT_NOTE_LEN]) -> TransparentNote { - TransparentNote::deserialize(preimage) -} - -fn serialize(note: TransparentNote) -> [Field; TRANSPARENT_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: TransparentNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: TransparentNote) -> Field { - note.compute_nullifier() -} - -fn get_header(note: TransparentNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut TransparentNote, header: NoteHeader) { - note.set_header(header) -} - -global TransparentNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - get_header, - set_header, -}; \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/test_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/test_contract/Nargo.toml index c4818f6ba32..afa5e911ac9 100644 --- a/yarn-project/noir-contracts/src/contracts/test_contract/Nargo.toml +++ b/yarn-project/noir-contracts/src/contracts/test_contract/Nargo.toml @@ -6,3 +6,4 @@ type = "contract" [dependencies] aztec = { path = "../../../../aztec-nr/aztec" } +token_portal_content_hash_lib = { path = "../token_portal_content_hash_lib" } diff --git a/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr b/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr index 51b6abea779..2a8addc00e9 100644 --- a/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr +++ b/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr @@ -39,6 +39,46 @@ impl TestPrivateContextInterface { } } + pub fn consume_mint_private_message( + self, + context: &mut PrivateContext, + secret_hash_for_redeeming_minted_notes: Field, + amount: Field, + canceller: Field, + msg_key: Field, + secret_for_L1_to_L2_message_consumption: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 5]; + serialized_args[0] = secret_hash_for_redeeming_minted_notes; + serialized_args[1] = amount; + serialized_args[2] = canceller; + serialized_args[3] = msg_key; + serialized_args[4] = secret_for_L1_to_L2_message_consumption; + + context.call_private_function(self.address, 0x8999306a, serialized_args) + } + + + pub fn consume_mint_public_message( + self, + context: &mut PrivateContext, + to: Field, + amount: Field, + canceller: Field, + msg_key: Field, + secret: Field + ) { + let mut serialized_args = [0; 5]; + serialized_args[0] = to; + serialized_args[1] = amount; + serialized_args[2] = canceller; + serialized_args[3] = msg_key; + serialized_args[4] = secret; + + context.call_public_function(self.address, 0x1c60863d, serialized_args) + } + + pub fn createL2ToL1MessagePublic( self, context: &mut PrivateContext, @@ -196,6 +236,26 @@ impl TestPublicContextInterface { } } + pub fn consume_mint_public_message( + self, + context: PublicContext, + to: Field, + amount: Field, + canceller: Field, + msg_key: Field, + secret: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 5]; + serialized_args[0] = to; + serialized_args[1] = amount; + serialized_args[2] = canceller; + serialized_args[3] = msg_key; + serialized_args[4] = secret; + + context.call_public_function(self.address, 0x1c60863d, serialized_args) + } + + pub fn createL2ToL1MessagePublic( self, context: PublicContext, diff --git a/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr index 0c4b7259046..7b527777416 100644 --- a/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr @@ -13,6 +13,7 @@ contract Test { types::vec::BoundedVec, constants_gen::EMPTY_NULLIFIED_COMMITMENT, }; + use dep::token_portal_content_hash_lib::{get_mint_private_content_hash, get_mint_public_content_hash}; #[event] struct ExampleEvent { @@ -137,6 +138,32 @@ contract Test { } // docs:end:emit_unencrypted_log + #[aztec(public)] + fn consume_mint_public_message( + to: Field, + amount: Field, + canceller: Field, + msg_key: Field, + secret: Field, + ) { + let content_hash = get_mint_public_content_hash(to, amount, canceller); + // Consume message and emit nullifier + context.consume_l1_to_l2_message(msg_key, content_hash, secret); + } + + #[aztec(private)] + fn consume_mint_private_message( + secret_hash_for_redeeming_minted_notes: Field, + amount: Field, + canceller: Field, + msg_key: Field, + secret_for_L1_to_L2_message_consumption: Field, + ) { + // Consume L1 to L2 message and emit nullifier + let content_hash = get_mint_private_content_hash(secret_hash_for_redeeming_minted_notes, amount, canceller); + context.consume_l1_to_l2_message(msg_key, content_hash, secret_for_L1_to_L2_message_consumption); + } + // Purely exists for testing unconstrained fn getRandom( kindaSeed: Field diff --git a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/Nargo.toml index cdad0512395..2dd5853eae1 100644 --- a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/Nargo.toml +++ b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/Nargo.toml @@ -5,4 +5,5 @@ compiler_version = "0.1" type = "contract" [dependencies] -aztec = { path = "../../../../aztec-nr/aztec" } \ No newline at end of file +aztec = { path = "../../../../aztec-nr/aztec" } +token_portal_content_hash_lib = { path = "../token_portal_content_hash_lib" } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr index 7febd157a16..a27049a7e4e 100644 --- a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr @@ -1,4 +1,3 @@ -mod util; mod token_interface; // Minimal implementation of the token bridge that can move funds between L1 <> L2. @@ -17,9 +16,9 @@ contract TokenBridge { types::address::{AztecAddress, EthereumAddress}, selector::compute_selector, }; + use dep::token_portal_content_hash_lib::{get_mint_public_content_hash, get_mint_private_content_hash, get_withdraw_content_hash}; use crate::token_interface::Token; - use crate::util::{get_mint_public_content_hash, get_mint_private_content_hash, get_withdraw_content_hash}; // Storage structure, containing all storage, and specifying what slots they use. struct Storage { @@ -91,14 +90,14 @@ contract TokenBridge { // User needs to call token.redeem_shield() to get the private assets #[aztec(private)] fn claim_private( - amount: Field, secret_hash_for_redeeming_minted_notes: Field, // secret hash used to redeem minted notes at a later time. This enables anyone to call this function and mint tokens to a user on their behalf + amount: Field, canceller: EthereumAddress, msg_key: Field, // L1 to L2 message key as derived from the inbox contract secret_for_L1_to_L2_message_consumption: Field, // secret used to consume the L1 to L2 message ) -> Field { // Consume L1 to L2 message and emit nullifier - let content_hash = get_mint_private_content_hash(amount, secret_hash_for_redeeming_minted_notes, canceller.address); + let content_hash = get_mint_private_content_hash(secret_hash_for_redeeming_minted_notes, amount, canceller.address); context.consume_l1_to_l2_message(msg_key, content_hash, secret_for_L1_to_L2_message_consumption); // Mint tokens on L2 @@ -119,8 +118,8 @@ contract TokenBridge { // Requires `msg.sender` (caller of the method) to give approval to the bridge to burn tokens on their behalf using witness signatures #[aztec(private)] fn exit_to_l1_private( - recipient: EthereumAddress, // ethereum address to withdraw to token: AztecAddress, + recipient: EthereumAddress, // ethereum address to withdraw to amount: Field, callerOnL1: EthereumAddress, // ethereum address that can call this function on the L1 portal (0x0 if anyone can call) nonce: Field, // nonce used in the approval message by `msg.sender` to let bridge burn their tokens on L2 diff --git a/yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/Nargo.toml b/yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/Nargo.toml new file mode 100644 index 00000000000..f3dfa0e4f2a --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "token_portal_content_hash_lib" +authors = ["aztec-labs"] +compiler_version = "0.1" +type = "lib" + +[dependencies] +aztec = { path = "../../../../aztec-nr/aztec" } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/util.nr b/yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/src/lib.nr similarity index 66% rename from yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/util.nr rename to yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/src/lib.nr index e58879f7034..f2a634b9f8c 100644 --- a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/util.nr +++ b/yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/src/lib.nr @@ -7,21 +7,21 @@ use dep::aztec::hash::{sha256_to_field}; pub fn get_mint_public_content_hash(owner_address: Field, amount: Field, canceller: Field) -> Field { let mut hash_bytes: [u8; 100] = [0; 100]; - let amount_bytes = amount.to_be_bytes(32); let recipient_bytes = owner_address.to_be_bytes(32); + let amount_bytes = amount.to_be_bytes(32); let canceller_bytes = canceller.to_be_bytes(32); for i in 0..32 { - hash_bytes[i + 4] = amount_bytes[i]; - hash_bytes[i + 36] = recipient_bytes[i]; + hash_bytes[i + 4] = recipient_bytes[i]; + hash_bytes[i + 36] = amount_bytes[i]; hash_bytes[i + 68] = canceller_bytes[i]; } - // Function selector: 0x63c9440d keccak256('mint_public(uint256,bytes32,address)') - hash_bytes[0] = 0x63; - hash_bytes[1] = 0xc9; - hash_bytes[2] = 0x44; - hash_bytes[3] = 0x0d; + // Function selector: 0xefc2aae6 keccak256('mint_public(bytes32,uint256,address)') + hash_bytes[0] = 0xef; + hash_bytes[1] = 0xc2; + hash_bytes[2] = 0xaa; + hash_bytes[3] = 0xe6; let content_hash = sha256_to_field(hash_bytes); content_hash @@ -30,23 +30,23 @@ pub fn get_mint_public_content_hash(owner_address: Field, amount: Field, cancell // Computes a content hash of a deposit/mint_private message. // Refer TokenPortal.sol for reference on L1. -pub fn get_mint_private_content_hash(amount: Field, secret_hash_for_redeeming_minted_notes: Field, canceller: Field) -> Field { +pub fn get_mint_private_content_hash(secret_hash_for_redeeming_minted_notes: Field, amount: Field, canceller: Field) -> Field { let mut hash_bytes: [u8; 100] = [0; 100]; - let amount_bytes = amount.to_be_bytes(32); let secret_hash_bytes = secret_hash_for_redeeming_minted_notes.to_be_bytes(32); + let amount_bytes = amount.to_be_bytes(32); let canceller_bytes = canceller.to_be_bytes(32); for i in 0..32 { - hash_bytes[i + 4] = amount_bytes[i]; - hash_bytes[i + 36] = secret_hash_bytes[i]; + hash_bytes[i + 4] = secret_hash_bytes[i]; + hash_bytes[i + 36] = amount_bytes[i]; hash_bytes[i + 68] = canceller_bytes[i]; } - // Function selector: 0x25d46b0f keccak256('mint_private(uint256,bytes32,address)') - hash_bytes[0] = 0x25; - hash_bytes[1] = 0xd4; - hash_bytes[2] = 0x6b; - hash_bytes[3] = 0x0f; + // Function selector: 0xf512262e keccak256('mint_private(bytes32,uint256,address)') + hash_bytes[0] = 0xf5; + hash_bytes[1] = 0x12; + hash_bytes[2] = 0x26; + hash_bytes[3] = 0x2e; let content_hash = sha256_to_field(hash_bytes); content_hash @@ -59,19 +59,19 @@ pub fn get_withdraw_content_hash(recipient: Field, amount: Field, callerOnL1: Fi // then convert to a single field element // add that to the l2 to l1 messages let mut hash_bytes: [u8; 100] = [0; 100]; - let amount_bytes = amount.to_be_bytes(32); let recipient_bytes = recipient.to_be_bytes(32); + let amount_bytes = amount.to_be_bytes(32); let callerOnL1_bytes = callerOnL1.to_be_bytes(32); - // 0xb460af94, selector for "withdraw(uint256,address,address)" - hash_bytes[0] = 0xb4; - hash_bytes[1] = 0x60; - hash_bytes[2] = 0xaf; - hash_bytes[3] = 0x94; + // 0x69328dec, selector for "withdraw(address,uint256,address)" + hash_bytes[0] = 0x69; + hash_bytes[1] = 0x32; + hash_bytes[2] = 0x8d; + hash_bytes[3] = 0xec; for i in 0..32 { - hash_bytes[i + 4] = amount_bytes[i]; - hash_bytes[i + 36] = recipient_bytes[i]; + hash_bytes[i + 4] = recipient_bytes[i]; + hash_bytes[i + 36] = amount_bytes[i]; hash_bytes[i + 68] = callerOnL1_bytes[i]; } let content_hash = sha256_to_field(hash_bytes); diff --git a/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/interfaces.nr b/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/interfaces.nr index 7742785694e..82c3a996114 100644 --- a/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/interfaces.nr +++ b/yarn-project/noir-contracts/src/contracts/uniswap_contract/src/interfaces.nr @@ -44,14 +44,6 @@ impl TokenBridge { AztecAddress::new(return_values[0]) } - pub fn claim_public(self: Self, context: PublicContext, to: Field, amount: Field, canceller: Field, msg_key: Field, secret: Field) { - let _return_values = context.call_public_function( - self.address, - compute_selector("claim_public((Field),Field,Field,Field,Field)"), - [to, amount, canceller, msg_key, secret] - ); - } - pub fn exit_to_l1_public(self: Self, context: PublicContext, recipient: Field, amount: Field, callerOnL1: Field, nonce: Field) { let _return_values = context.call_public_function( self.address, 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 ce007c5672c..2d372eca1cd 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 @@ -97,6 +97,7 @@ contract Uniswap { // this ensures the integrity of what the user originally intends to do on L1. let input_asset_bridge_portal_address = get_portal_address(input_asset_bridge.address); let output_asset_bridge_portal_address = get_portal_address(output_asset_bridge.address); + // ensure portal exists - else funds might be lost assert(input_asset_bridge_portal_address != 0, "L1 portal address of input_asset's bridge is 0"); assert(output_asset_bridge_portal_address != 0, "L1 portal address of output_asset's bridge is 0"); @@ -160,6 +161,7 @@ contract Uniswap { // this ensures the integrity of what the user originally intends to do on L1. let input_asset_bridge_portal_address = get_portal_address(input_asset_bridge.address); let output_asset_bridge_portal_address = get_portal_address(output_asset_bridge.address); + // ensure portal exists - else funds might be lost assert(input_asset_bridge_portal_address != 0, "L1 portal address of input_asset's bridge is 0"); assert(output_asset_bridge_portal_address != 0, "L1 portal address of output_asset's bridge is 0"); diff --git a/yarn-project/noir-contracts/src/scripts/copy_output.ts b/yarn-project/noir-contracts/src/scripts/copy_output.ts index 352fa339e3d..ab22356465e 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: 'EcdsaAccount', target: '../aztec.js/src/artifacts/', exclude: [] }, ]; -const INTERFACE_CONTRACTS = ['private_token', 'private_token_airdrop', 'non_native_token', 'test']; +const INTERFACE_CONTRACTS = ['private_token', 'private_token_airdrop', 'test']; /** * Writes the contract to a specific project folder, if needed. diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 8de88d3e484..dc67e594d44 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -82,6 +82,7 @@ __metadata: ts-node: ^10.9.1 tslib: ^2.4.0 typescript: ^5.0.4 + viem: ^1.2.5 languageName: unknown linkType: soft