diff --git a/l1-contracts/src/core/RollupCore.sol b/l1-contracts/src/core/RollupCore.sol index 6a5172538728..eb443a15929c 100644 --- a/l1-contracts/src/core/RollupCore.sol +++ b/l1-contracts/src/core/RollupCore.sol @@ -2,10 +2,8 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; import { - IRollup, IRollupCore, ITestRollup, CheatDepositArgs, @@ -98,14 +96,17 @@ contract RollupCore is ) ); rollupStore.config.version = version; - rollupStore.config.inbox = - IInbox(address(new Inbox(address(this), version, Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT))); - rollupStore.config.outbox = IOutbox(address(new Outbox(address(this), version))); - rollupStore.config.feeAssetPortal = IFeeJuicePortal( - new FeeJuicePortal(IRollup(address(this)), _feeAsset, rollupStore.config.inbox, version) + IInbox inbox = IInbox( + address(new Inbox(address(this), _feeAsset, version, Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT)) ); + rollupStore.config.inbox = inbox; + + rollupStore.config.outbox = IOutbox(address(new Outbox(address(this), version))); + + rollupStore.config.feeAssetPortal = IFeeJuicePortal(inbox.getFeeAssetPortal()); + FeeLib.initialize(_config.manaTarget, _config.provingCostPerMana); } diff --git a/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol b/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol index b50108c2df29..bb2e78555aa5 100644 --- a/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol +++ b/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol @@ -12,7 +12,6 @@ interface IFeeJuicePortal { ); event FeesDistributed(address indexed to, uint256 amount); - function initialize() external; function distributeFees(address _to, uint256 _amount) external; function depositToAztecPublic(bytes32 _to, uint256 _amount, bytes32 _secretHash) external diff --git a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol index 8c658a1264f2..a1d3d808c176 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol @@ -48,5 +48,7 @@ interface IInbox { function consume(uint256 _toConsume) external returns (bytes32); // docs:end:consume + function getFeeAssetPortal() external view returns (address); + function getRoot(uint256 _blockNumber) external view returns (bytes32); } diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 71baf008b764..37f97b7b761a 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -100,7 +100,6 @@ library Constants { 20646204262468251631976884937192820660867507115079672078981654411421834866549; uint256 internal constant GENESIS_ARCHIVE_ROOT = 1002640778211850180189505934749257244705296832326768971348723156503780793518; - uint256 internal constant FEE_JUICE_INITIAL_MINT = 200000000000000000000000; uint256 internal constant PUBLIC_DISPATCH_SELECTOR = 3578010381; uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000; uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; diff --git a/l1-contracts/src/core/FeeJuicePortal.sol b/l1-contracts/src/core/messagebridge/FeeJuicePortal.sol similarity index 79% rename from l1-contracts/src/core/FeeJuicePortal.sol rename to l1-contracts/src/core/messagebridge/FeeJuicePortal.sol index 288d8ded461a..93e00c0067fa 100644 --- a/l1-contracts/src/core/FeeJuicePortal.sol +++ b/l1-contracts/src/core/messagebridge/FeeJuicePortal.sol @@ -22,8 +22,6 @@ contract FeeJuicePortal is IFeeJuicePortal { IERC20 public immutable UNDERLYING; uint256 public immutable VERSION; - bool public initialized; - constructor(IRollup _rollup, IERC20 _underlying, IInbox _inbox, uint256 _version) { ROLLUP = _rollup; INBOX = _inbox; @@ -31,26 +29,6 @@ contract FeeJuicePortal is IFeeJuicePortal { VERSION = _version; } - /** - * @notice Initialize the FeeJuicePortal - * - * @dev This function is only callable by the owner of the contract and only once - * - * @dev Must be funded with FEE_JUICE_INITIAL_MINT tokens before initialization to - * ensure that the L2 contract is funded and able to pay for its deployment. - */ - function initialize() external override(IFeeJuicePortal) { - require(!initialized, Errors.FeeJuicePortal__AlreadyInitialized()); - - uint256 balance = UNDERLYING.balanceOf(address(this)); - if (balance < Constants.FEE_JUICE_INITIAL_MINT) { - UNDERLYING.safeTransferFrom( - msg.sender, address(this), Constants.FEE_JUICE_INITIAL_MINT - balance - ); - } - initialized = true; - } - /** * @notice Deposit funds into the portal and adds an L2 message which can only be consumed publicly on Aztec * @param _to - The aztec address of the recipient diff --git a/l1-contracts/src/core/messagebridge/Inbox.sol b/l1-contracts/src/core/messagebridge/Inbox.sol index 8998d1656785..b57e76150c21 100644 --- a/l1-contracts/src/core/messagebridge/Inbox.sol +++ b/l1-contracts/src/core/messagebridge/Inbox.sol @@ -2,12 +2,16 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; +import {IRollup} from "@aztec/core/interfaces/IRollup.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; +import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {FrontierLib} from "@aztec/core/libraries/crypto/FrontierLib.sol"; import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; /** * @title Inbox @@ -21,6 +25,7 @@ contract Inbox is IInbox { address public immutable ROLLUP; uint256 public immutable VERSION; + address public immutable FEE_ASSET_PORTAL; uint256 internal immutable HEIGHT; uint256 internal immutable SIZE; @@ -38,7 +43,7 @@ contract Inbox is IInbox { // as it can more easily figure out if it can just skip looking for events for a time period. uint256 public totalMessagesInserted = 0; - constructor(address _rollup, uint256 _version, uint256 _height) { + constructor(address _rollup, IERC20 _feeAsset, uint256 _version, uint256 _height) { ROLLUP = _rollup; VERSION = _version; @@ -47,6 +52,9 @@ contract Inbox is IInbox { forest.initialize(_height); EMPTY_ROOT = trees[inProgress].root(forest, HEIGHT, SIZE); + + FEE_ASSET_PORTAL = + address(new FeeJuicePortal(IRollup(_rollup), _feeAsset, IInbox(this), VERSION)); } /** @@ -90,8 +98,14 @@ contract Inbox is IInbox { // trees are constant size so global index = tree number * size + subtree index uint256 index = (inProgress - Constants.INITIAL_L2_BLOCK_NUM) * SIZE + currentTree.nextIndex; + // If the sender is the fee asset portal, we use a magic address to simpler have it initialized at genesis. + // We assume that no-one will know the private key for this address and that the precompile won't change to + // make calls into arbitrary contracts. + address senderAddress = + msg.sender == FEE_ASSET_PORTAL ? address(uint160(Constants.FEE_JUICE_ADDRESS)) : msg.sender; + DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({ - sender: DataStructures.L1Actor(msg.sender, block.chainid), + sender: DataStructures.L1Actor(senderAddress, block.chainid), recipient: _recipient, content: _content, secretHash: _secretHash, @@ -134,6 +148,10 @@ contract Inbox is IInbox { return root; } + function getFeeAssetPortal() external view override(IInbox) returns (address) { + return FEE_ASSET_PORTAL; + } + function getRoot(uint256 _blockNumber) external view override(IInbox) returns (bytes32) { return trees[_blockNumber].root(forest, HEIGHT, SIZE); } diff --git a/l1-contracts/src/mock/MockFeeJuicePortal.sol b/l1-contracts/src/mock/MockFeeJuicePortal.sol index 2a9021ba255a..e742025fbac5 100644 --- a/l1-contracts/src/mock/MockFeeJuicePortal.sol +++ b/l1-contracts/src/mock/MockFeeJuicePortal.sol @@ -19,8 +19,6 @@ contract MockFeeJuicePortal is IFeeJuicePortal { UNDERLYING = new TestERC20("test", "TEST", msg.sender); } - function initialize() external override(IFeeJuicePortal) {} - function distributeFees(address, uint256) external override(IFeeJuicePortal) {} function depositToAztecPublic(bytes32, uint256, bytes32) diff --git a/l1-contracts/test/Inbox.t.sol b/l1-contracts/test/Inbox.t.sol index 5d0cec26cf3e..ff225eb30a49 100644 --- a/l1-contracts/test/Inbox.t.sol +++ b/l1-contracts/test/Inbox.t.sol @@ -3,7 +3,8 @@ pragma solidity >=0.8.27; import {Test} from "forge-std/Test.sol"; - +import {TestERC20} from "src/mock/TestERC20.sol"; +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {InboxHarness} from "./harnesses/InboxHarness.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; @@ -26,7 +27,8 @@ contract InboxTest is Test { function setUp() public { address rollup = address(this); - inbox = new InboxHarness(rollup, version, HEIGHT); + IERC20 feeAsset = new TestERC20("Fee Asset", "FA", address(this)); + inbox = new InboxHarness(rollup, feeAsset, version, HEIGHT); emptyTreeRoot = inbox.getEmptyRoot(); } diff --git a/l1-contracts/test/MultiProof.t.sol b/l1-contracts/test/MultiProof.t.sol index 986c9cbbc129..13a3b8c52952 100644 --- a/l1-contracts/test/MultiProof.t.sol +++ b/l1-contracts/test/MultiProof.t.sol @@ -7,7 +7,7 @@ import {DecoderBase} from "./base/DecoderBase.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Registry} from "@aztec/governance/Registry.sol"; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; import {TestConstants} from "./harnesses/TestConstants.sol"; import {RewardDistributor} from "@aztec/governance/RewardDistributor.sol"; @@ -90,9 +90,6 @@ contract MultiProofTest is RollupBase { feeJuicePortal = FeeJuicePortal(address(rollup.getFeeAssetPortal())); - testERC20.mint(address(feeJuicePortal), Constants.FEE_JUICE_INITIAL_MINT); - feeJuicePortal.initialize(); - registry.addRollup(IRollup(address(rollup))); _; @@ -137,6 +134,9 @@ contract MultiProofTest is RollupBase { address alice = address(bytes20("alice")); address bob = address(bytes20("bob")); + // We need to mint some fee asset to the portal to cover the 30M mana spent. + deal(address(testERC20), address(feeJuicePortal), 30e6 * 1e18); + _proposeBlock("mixed_block_1", 1, 15e6); _proposeBlock("mixed_block_2", 2, 15e6); diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 09c5798ef115..ec0f4029c82c 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -26,7 +26,7 @@ import { FeeAssetPerEthE9, PublicInputArgs } from "@aztec/core/interfaces/IRollup.sol"; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; import {NaiveMerkle} from "./merkle/Naive.sol"; import {MerkleTestUtil} from "./merkle/TestUtil.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; @@ -108,9 +108,6 @@ contract RollupTest is RollupBase { feeJuicePortal = FeeJuicePortal(address(rollup.getFeeAssetPortal())); - testERC20.mint(address(feeJuicePortal), Constants.FEE_JUICE_INITIAL_MINT); - feeJuicePortal.initialize(); - merkleTestUtil = new MerkleTestUtil(); _; } @@ -349,6 +346,9 @@ contract RollupTest is RollupBase { } function testProvingFeeUpdates() public setUpFor("mixed_block_1") { + // We need to mint some fee asset to the portal to cover the 2M mana spent. + deal(address(testERC20), address(feeJuicePortal), 2e6 * 1e18); + rollup.setProvingCostPerMana(EthValue.wrap(1000)); _proposeBlock("mixed_block_1", 1, 1e6); diff --git a/l1-contracts/test/benchmark/happy.t.sol b/l1-contracts/test/benchmark/happy.t.sol index d4a5e2ba8d07..148bd273ba8e 100644 --- a/l1-contracts/test/benchmark/happy.t.sol +++ b/l1-contracts/test/benchmark/happy.t.sol @@ -23,7 +23,7 @@ import { PublicInputArgs, RollupConfigInput } from "@aztec/core/interfaces/IRollup.sol"; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; import {NaiveMerkle} from "../merkle/Naive.sol"; import {MerkleTestUtil} from "../merkle/TestUtil.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; diff --git a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol index ce81143a547d..e3f4996b2c4b 100644 --- a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol +++ b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.27; import {Test} from "forge-std/Test.sol"; import {Registry} from "@aztec/governance/Registry.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; @@ -27,6 +27,8 @@ contract DepositToAztecPublic is Test { Rollup internal rollup; RewardDistributor internal rewardDistributor; + address internal constant MAGIC_FEE_JUICE_ADDRESS = address(uint160(Constants.FEE_JUICE_ADDRESS)); + function setUp() public { token = new TestERC20("test", "TEST", address(this)); registry = new Registry(OWNER, token); @@ -42,8 +44,6 @@ contract DepositToAztecPublic is Test { ); feeJuicePortal = FeeJuicePortal(address(rollup.getFeeAssetPortal())); - token.mint(address(feeJuicePortal), Constants.FEE_JUICE_INITIAL_MINT); - feeJuicePortal.initialize(); vm.prank(OWNER); registry.addRollup(IRollup(address(rollup))); @@ -79,8 +79,10 @@ contract DepositToAztecPublic is Test { // The purpose of including the function selector is to make the message unique to that specific call. Note that // it has nothing to do with calling the function. + // Separately, NOTE that the sender is the MAGIC_FEE_JUICE_ADDRESS, not the feeJuicePortal address in + // this special case. DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({ - sender: DataStructures.L1Actor(address(feeJuicePortal), block.chainid), + sender: DataStructures.L1Actor(MAGIC_FEE_JUICE_ADDRESS, block.chainid), recipient: DataStructures.L2Actor(feeJuicePortal.L2_TOKEN_ADDRESS(), rollup.getVersion()), content: Hash.sha256ToField(abi.encodeWithSignature("claim(bytes32,uint256)", to, amount)), secretHash: secretHash, diff --git a/l1-contracts/test/fee_portal/distributeFees.t.sol b/l1-contracts/test/fee_portal/distributeFees.t.sol index 6a78f4ec5aa7..2a26b07d820e 100644 --- a/l1-contracts/test/fee_portal/distributeFees.t.sol +++ b/l1-contracts/test/fee_portal/distributeFees.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.27; import {Test} from "forge-std/Test.sol"; import {Registry} from "@aztec/governance/Registry.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; @@ -41,8 +41,6 @@ contract DistributeFees is Test { ); feeJuicePortal = FeeJuicePortal(address(rollup.getFeeAssetPortal())); - token.mint(address(feeJuicePortal), Constants.FEE_JUICE_INITIAL_MINT); - feeJuicePortal.initialize(); vm.prank(OWNER); registry.addRollup(IRollup(address(rollup))); @@ -63,13 +61,10 @@ contract DistributeFees is Test { vm.prank(address(rollup)); vm.expectRevert( abi.encodeWithSelector( - IERC20Errors.ERC20InsufficientBalance.selector, - address(feeJuicePortal), - Constants.FEE_JUICE_INITIAL_MINT, - Constants.FEE_JUICE_INITIAL_MINT + 1 + IERC20Errors.ERC20InsufficientBalance.selector, address(feeJuicePortal), 0, 0 + 1 ) ); - feeJuicePortal.distributeFees(address(this), Constants.FEE_JUICE_INITIAL_MINT + 1); + feeJuicePortal.distributeFees(address(this), 1); } function test_GivenSufficientBalance() external givenTheCallerIsTheCanonicalRollup { @@ -78,11 +73,15 @@ contract DistributeFees is Test { assertEq(token.balanceOf(address(this)), 0); + uint256 initialBalance = 10e18; + + deal(address(token), address(feeJuicePortal), initialBalance); + vm.prank(address(rollup)); vm.expectEmit(true, true, true, true, address(feeJuicePortal)); - emit IFeeJuicePortal.FeesDistributed(address(this), Constants.FEE_JUICE_INITIAL_MINT); - feeJuicePortal.distributeFees(address(this), Constants.FEE_JUICE_INITIAL_MINT); + emit IFeeJuicePortal.FeesDistributed(address(this), initialBalance); + feeJuicePortal.distributeFees(address(this), initialBalance); - assertEq(token.balanceOf(address(this)), Constants.FEE_JUICE_INITIAL_MINT); + assertEq(token.balanceOf(address(this)), initialBalance); } } diff --git a/l1-contracts/test/fees/FeeRollup.t.sol b/l1-contracts/test/fees/FeeRollup.t.sol index 8a8bad6e31ea..2462ac6d48d3 100644 --- a/l1-contracts/test/fees/FeeRollup.t.sol +++ b/l1-contracts/test/fees/FeeRollup.t.sol @@ -22,7 +22,7 @@ import { PublicInputArgs, RollupConfigInput } from "@aztec/core/interfaces/IRollup.sol"; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; import {NaiveMerkle} from "../merkle/Naive.sol"; import {MerkleTestUtil} from "../merkle/TestUtil.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; diff --git a/l1-contracts/test/harnesses/InboxHarness.sol b/l1-contracts/test/harnesses/InboxHarness.sol index 41bc14a92fa5..532f572bc900 100644 --- a/l1-contracts/test/harnesses/InboxHarness.sol +++ b/l1-contracts/test/harnesses/InboxHarness.sol @@ -3,14 +3,16 @@ pragma solidity >=0.8.27; import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; - +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {FrontierLib} from "@aztec/core/libraries/crypto/FrontierLib.sol"; contract InboxHarness is Inbox { using FrontierLib for FrontierLib.Tree; - constructor(address _rollup, uint256 _version, uint256 _height) Inbox(_rollup, _version, _height) {} + constructor(address _rollup, IERC20 _feeAsset, uint256 _version, uint256 _height) + Inbox(_rollup, _feeAsset, _version, _height) + {} function getSize() external view returns (uint256) { return SIZE; diff --git a/l1-contracts/test/ignition.t.sol b/l1-contracts/test/ignition.t.sol index 86a1fbf7efb9..649a182a5dbc 100644 --- a/l1-contracts/test/ignition.t.sol +++ b/l1-contracts/test/ignition.t.sol @@ -7,7 +7,7 @@ import {DecoderBase} from "./base/DecoderBase.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Registry} from "@aztec/governance/Registry.sol"; -import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; +import {FeeJuicePortal} from "@aztec/core/messagebridge/FeeJuicePortal.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; import {TestConstants} from "./harnesses/TestConstants.sol"; import {RewardDistributor} from "@aztec/governance/RewardDistributor.sol"; @@ -93,8 +93,6 @@ contract IgnitionTest is RollupBase { ); feeJuicePortal = FeeJuicePortal(address(rollup.getFeeAssetPortal())); - testERC20.mint(address(feeJuicePortal), Constants.FEE_JUICE_INITIAL_MINT); - feeJuicePortal.initialize(); rewardDistributor = RewardDistributor(address(registry.getRewardDistributor())); testERC20.mint(address(rewardDistributor), 1e6 ether); diff --git a/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr index 8041d8af39e0..866e8e975aac 100644 --- a/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr @@ -6,8 +6,12 @@ use dep::aztec::macros::aztec; pub contract FeeJuice { use dep::aztec::{ macros::{functions::{internal, private, public, view}, storage::storage}, - protocol_types::address::{AztecAddress, EthAddress}, - state_vars::{Map, PublicImmutable, PublicMutable}, + protocol_types::{ + address::{AztecAddress, EthAddress}, + constants::FEE_JUICE_ADDRESS, + traits::ToField, + }, + state_vars::{Map, PublicMutable}, }; use crate::lib::get_bridge_gas_msg_hash; @@ -18,38 +22,12 @@ pub contract FeeJuice { // This map is accessed directly by protocol circuits to check balances for fee payment. // Do not change this storage layout unless you also update the base rollup circuits. balances: Map, Context>, - portal_address: PublicImmutable, - } - - // Not flagged as initializer to reduce cost of checking init nullifier in all functions. - // This function should be called as entrypoint to initialize the contract by minting itself funds. - #[private] - fn initialize(portal_address: EthAddress, initial_mint: u128) { - // Validate contract class parameters are correct - let self = context.this_address(); - - // Increase self balance and set as fee payer, and end setup - FeeJuice::at(self)._increase_public_balance(self, initial_mint).enqueue(&mut context); - context.set_as_fee_payer(); - context.end_setup(); - - // Enqueue call to set the portal address - FeeJuice::at(self).set_portal(portal_address).enqueue(&mut context); - } - - // We purposefully not set this function as an initializer so we do not bind - // the contract to a specific L1 portal address, since the Fee Juice address - // is a hardcoded constant in the rollup circuits. - #[public] - fn set_portal(portal_address: EthAddress) { - assert(storage.portal_address.read().is_zero()); - storage.portal_address.initialize(portal_address); } #[private] fn claim(to: AztecAddress, amount: u128, secret: Field, message_leaf_index: Field) { let content_hash = get_bridge_gas_msg_hash(to, amount); - let portal_address = storage.portal_address.read(); + let portal_address = EthAddress::from_field(FEE_JUICE_ADDRESS.to_field()); assert(!portal_address.is_zero()); // Consume message and emit nullifier diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 25d7d1abd7ad..ec03a4d6cac0 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -138,9 +138,6 @@ pub global GENESIS_BLOCK_HASH: Field = 0x2da55666630fdf8594065c377958c827dc1c130dac91f17c6699b53dce60ef75; pub global GENESIS_ARCHIVE_ROOT: Field = 0x0237797d6a2c04d20d4fa06b74482bd970ccd51a43d9b05b57e9b91fa1ae1cae; -// The following and the value in `deploy_l1_contracts` must match. We should not have the code both places, but -// we are running into circular dependency issues. #3342 -pub global FEE_JUICE_INITIAL_MINT: Field = 200000000000000000000000; // Last 4 bytes of the Poseidon2 hash of 'public_dispatch(Field)'. pub global PUBLIC_DISPATCH_SELECTOR: Field = 0xd5441b0d; diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts index 154ac35f6f5d..15f6fcb10d12 100644 --- a/yarn-project/aztec/src/cli/cmds/start_node.ts +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -54,7 +54,9 @@ export async function startNode( userLog(`Initial funded accounts: ${initialFundedAccounts.map(a => a.toString()).join(', ')}`); - const { genesisBlockHash, genesisArchiveRoot, prefilledPublicData } = await getGenesisValues(initialFundedAccounts); + const { genesisBlockHash, genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues( + initialFundedAccounts, + ); userLog(`Genesis block hash: ${genesisBlockHash.toString()}`); userLog(`Genesis archive root: ${genesisArchiveRoot.toString()}`); @@ -75,6 +77,7 @@ export async function startNode( salt: nodeSpecificOptions.deployAztecContractsSalt, genesisBlockHash, genesisArchiveRoot, + feeJuicePortalInitialBalance: fundingNeeded, }); } // If not deploying, validate that any addresses and config provided are correct. diff --git a/yarn-project/aztec/src/sandbox/sandbox.ts b/yarn-project/aztec/src/sandbox/sandbox.ts index f1b33a60c718..4c72ea5f00d7 100644 --- a/yarn-project/aztec/src/sandbox/sandbox.ts +++ b/yarn-project/aztec/src/sandbox/sandbox.ts @@ -4,7 +4,7 @@ import { deployFundedSchnorrAccounts, getInitialTestAccounts } from '@aztec/acco import { type AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AnvilTestWatcher, EthCheatCodes } from '@aztec/aztec.js/testing'; import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client'; -import { setupCanonicalL2FeeJuice, setupSponsoredFPC } from '@aztec/cli/cli-utils'; +import { setupSponsoredFPC } from '@aztec/cli/cli-utils'; import { GENESIS_ARCHIVE_ROOT, GENESIS_BLOCK_HASH } from '@aztec/constants'; import { NULL_KEY, @@ -16,7 +16,7 @@ import { import { Fr } from '@aztec/foundation/fields'; import { type LogFn, createLogger } from '@aztec/foundation/log'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts'; +import { protocolContractTreeRoot } from '@aztec/protocol-contracts'; import { type PXEServiceConfig, createPXEService, getPXEServiceConfig } from '@aztec/pxe/server'; import type { AztecNode } from '@aztec/stdlib/interfaces/client'; import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees'; @@ -49,7 +49,13 @@ export async function deployContractsToL1( aztecNodeConfig: AztecNodeConfig, hdAccount: HDAccount | PrivateKeyAccount, contractDeployLogger = logger, - opts: { assumeProvenThroughBlockNumber?: number; salt?: number; genesisArchiveRoot?: Fr; genesisBlockHash?: Fr } = {}, + opts: { + assumeProvenThroughBlockNumber?: number; + salt?: number; + genesisArchiveRoot?: Fr; + genesisBlockHash?: Fr; + feeJuicePortalInitialBalance?: bigint; + } = {}, ) { const chain = aztecNodeConfig.l1RpcUrls.length > 0 @@ -66,12 +72,12 @@ export async function deployContractsToL1( { ...getL1ContractsConfigEnvVars(), // TODO: We should not need to be loading config from env again, caller should handle this ...aztecNodeConfig, - l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, genesisArchiveRoot: opts.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT), genesisBlockHash: opts.genesisBlockHash ?? new Fr(GENESIS_BLOCK_HASH), salt: opts.salt, + feeJuicePortalInitialBalance: opts.feeJuicePortalInitialBalance, }, ); @@ -134,7 +140,9 @@ export async function createSandbox(config: Partial = {}, userLog const fundedAddresses = initialAccounts.length ? [...initialAccounts.map(a => a.address), bananaFPC, sponsoredFPC] : []; - const { genesisArchiveRoot, genesisBlockHash, prefilledPublicData } = await getGenesisValues(fundedAddresses); + const { genesisArchiveRoot, genesisBlockHash, prefilledPublicData, fundingNeeded } = await getGenesisValues( + fundedAddresses, + ); let watcher: AnvilTestWatcher | undefined = undefined; if (!aztecNodeConfig.p2pEnabled) { @@ -143,6 +151,7 @@ export async function createSandbox(config: Partial = {}, userLog genesisArchiveRoot, genesisBlockHash, salt: config.l1Salt ? parseInt(config.l1Salt) : undefined, + feeJuicePortalInitialBalance: fundingNeeded, }); const chain = @@ -167,8 +176,6 @@ export async function createSandbox(config: Partial = {}, userLog const pxeServiceConfig = { proverEnabled: aztecNodeConfig.realProofs }; const pxe = await createAztecPXE(node, pxeServiceConfig); - await setupCanonicalL2FeeJuice(pxe, aztecNodeConfig.l1Contracts.feeJuicePortalAddress, logger.info); - if (initialAccounts.length) { userLog('Setting up funded test accounts...'); const accounts = await deployFundedSchnorrAccounts(pxe, initialAccounts); diff --git a/yarn-project/cli/src/cmds/infrastructure/setup_l2_contract.ts b/yarn-project/cli/src/cmds/infrastructure/setup_l2_contract.ts index a38f7714d09e..e696f617d12f 100644 --- a/yarn-project/cli/src/cmds/infrastructure/setup_l2_contract.ts +++ b/yarn-project/cli/src/cmds/infrastructure/setup_l2_contract.ts @@ -4,7 +4,7 @@ import { jsonStringify } from '@aztec/foundation/json-rpc'; import type { LogFn } from '@aztec/foundation/log'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; -import { setupCanonicalL2FeeJuice, setupSponsoredFPC } from '../../utils/setup_contracts.js'; +import { setupSponsoredFPC } from '../../utils/setup_contracts.js'; export async function setupL2Contracts( rpcUrl: string, @@ -30,12 +30,6 @@ export async function setupL2Contracts( log('setupL2Contracts: Creating PXE client...'); const pxe = createPXEClient(rpcUrl, {}, makeFetch([1, 1, 1, 1, 1], false)); - log('setupL2Contracts: Getting fee juice portal address...'); - // Deploy Fee Juice - const feeJuicePortalAddress = (await pxe.getNodeInfo()).l1ContractAddresses.feeJuicePortalAddress; - log('setupL2Contracts: Setting up fee juice portal...'); - await setupCanonicalL2FeeJuice(pxe, feeJuicePortalAddress, log, waitOpts, waitForProvenOptions); - let deployedAccounts: InitialAccountData[] = []; if (testAccounts) { log('setupL2Contracts: Deploying test accounts...'); diff --git a/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts b/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts index 0080535a82b9..7d52c908f7e2 100644 --- a/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts +++ b/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts @@ -27,7 +27,7 @@ export async function deployL1Contracts( const initialAccounts = testAccounts ? await getInitialTestAccounts() : []; const sponsoredFPCAddress = sponsoredFPC ? await getSponsoredFPCAddress() : []; const initialFundedAccounts = initialAccounts.map(a => a.address).concat(sponsoredFPCAddress); - const { genesisBlockHash, genesisArchiveRoot } = await getGenesisValues(initialFundedAccounts); + const { genesisBlockHash, genesisArchiveRoot, fundingNeeded } = await getGenesisValues(initialFundedAccounts); const { l1ContractAddresses } = await deployAztecContracts( rpcUrls, @@ -39,6 +39,7 @@ export async function deployL1Contracts( initialValidators, genesisArchiveRoot, genesisBlockHash, + fundingNeeded, acceleratedTestDeployments, config, debugLogger, diff --git a/yarn-project/cli/src/cmds/l1/deploy_new_rollup.ts b/yarn-project/cli/src/cmds/l1/deploy_new_rollup.ts index 208f8bf1246d..b89215dd83f8 100644 --- a/yarn-project/cli/src/cmds/l1/deploy_new_rollup.ts +++ b/yarn-project/cli/src/cmds/l1/deploy_new_rollup.ts @@ -27,7 +27,7 @@ export async function deployNewRollup( const initialAccounts = testAccounts ? await getInitialTestAccounts() : []; const sponsoredFPCAddress = sponsoredFPC ? await getSponsoredFPCAddress() : []; const initialFundedAccounts = initialAccounts.map(a => a.address).concat(sponsoredFPCAddress); - const { genesisBlockHash, genesisArchiveRoot } = await getGenesisValues(initialFundedAccounts); + const { genesisBlockHash, genesisArchiveRoot, fundingNeeded } = await getGenesisValues(initialFundedAccounts); const { rollup, slashFactoryAddress } = await deployNewRollupContracts( registryAddress, @@ -40,6 +40,7 @@ export async function deployNewRollup( initialValidators, genesisArchiveRoot, genesisBlockHash, + fundingNeeded, config, debugLogger, ); diff --git a/yarn-project/cli/src/utils/aztec.ts b/yarn-project/cli/src/utils/aztec.ts index a4fa5c46e9e8..6b6e2c8a0a0a 100644 --- a/yarn-project/cli/src/utils/aztec.ts +++ b/yarn-project/cli/src/utils/aztec.ts @@ -10,7 +10,7 @@ import type { DeployL1ContractsReturnType, L1ContractsConfig, RollupContract } f import type { Fr } from '@aztec/foundation/fields'; import type { LogFn, Logger } from '@aztec/foundation/log'; import type { NoirPackageConfig } from '@aztec/foundation/noir'; -import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts'; +import { protocolContractTreeRoot } from '@aztec/protocol-contracts'; import TOML from '@iarna/toml'; import { readFile } from 'fs/promises'; @@ -49,6 +49,7 @@ export async function deployAztecContracts( initialValidators: EthAddress[], genesisArchiveRoot: Fr, genesisBlockHash: Fr, + feeJuicePortalInitialBalance: bigint, acceleratedTestDeployments: boolean, config: L1ContractsConfig, debugLogger: Logger, @@ -69,7 +70,6 @@ export async function deployAztecContracts( chain.chainInfo, debugLogger, { - l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, genesisArchiveRoot, @@ -77,6 +77,7 @@ export async function deployAztecContracts( salt, initialValidators, acceleratedTestDeployments, + feeJuicePortalInitialBalance, ...config, }, config, @@ -94,6 +95,7 @@ export async function deployNewRollupContracts( initialValidators: EthAddress[], genesisArchiveRoot: Fr, genesisBlockHash: Fr, + feeJuicePortalInitialBalance: bigint, config: L1ContractsConfig, logger: Logger, ): Promise<{ rollup: RollupContract; slashFactoryAddress: EthAddress }> { @@ -113,10 +115,10 @@ export async function deployNewRollupContracts( salt, vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, - l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), genesisArchiveRoot, genesisBlockHash, initialValidators, + feeJuicePortalInitialBalance, ...config, }, registryAddress, diff --git a/yarn-project/cli/src/utils/setup_contracts.ts b/yarn-project/cli/src/utils/setup_contracts.ts index 4be1f46835d1..fcfe1077f9ba 100644 --- a/yarn-project/cli/src/utils/setup_contracts.ts +++ b/yarn-project/cli/src/utils/setup_contracts.ts @@ -1,7 +1,5 @@ import { DefaultWaitOpts, - type EthAddress, - FeeJuicePaymentMethod, Fr, type PXE, SignerlessWallet, @@ -10,52 +8,9 @@ import { getContractInstanceFromDeployParams, waitForProven, } from '@aztec/aztec.js'; -import { FEE_JUICE_INITIAL_MINT, SPONSORED_FPC_SALT } from '@aztec/constants'; +import { SPONSORED_FPC_SALT } from '@aztec/constants'; import { DefaultMultiCallEntrypoint } from '@aztec/entrypoints/multicall'; import type { LogFn } from '@aztec/foundation/log'; -import { ProtocolContractAddress } from '@aztec/protocol-contracts'; -import { Gas } from '@aztec/stdlib/gas'; - -/** - * Deploys the contract to pay for gas on L2. - */ -export async function setupCanonicalL2FeeJuice( - pxe: PXE, - feeJuicePortalAddress: EthAddress, - log: LogFn, - waitOpts = DefaultWaitOpts, - waitForProvenOptions?: WaitForProvenOpts, -) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment - const { FeeJuiceContract } = await import('@aztec/noir-contracts.js/FeeJuice'); - - const deployer = new SignerlessWallet(pxe); - - const feeJuiceContract = await FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, deployer); - - const portalAddress = await pxe.getPublicStorageAt( - feeJuiceContract.address, - feeJuiceContract.artifact.storageLayout.portal_address.slot, - ); - - if (portalAddress.isZero()) { - log('setupCanonicalL2FeeJuice: Calling initialize on fee juice contract...'); - const paymentMethod = new FeeJuicePaymentMethod(ProtocolContractAddress.FeeJuice); - const receipt = await feeJuiceContract.methods - .initialize(feeJuicePortalAddress, FEE_JUICE_INITIAL_MINT) - .send({ fee: { paymentMethod, gasSettings: { teardownGasLimits: Gas.empty() } } }) - .wait(waitOpts); - if (waitForProvenOptions !== undefined) { - await waitForProven(pxe, receipt, waitForProvenOptions); - } - } else { - log( - 'setupCanonicalL2FeeJuice: Fee juice contract already initialized. Fee Juice Portal address: ' + - portalAddress.toString(), - ); - } -} async function getSponsoredFPCContract() { // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/yarn-project/constants/src/constants.gen.ts b/yarn-project/constants/src/constants.gen.ts index 41c87df959bf..71dbae720697 100644 --- a/yarn-project/constants/src/constants.gen.ts +++ b/yarn-project/constants/src/constants.gen.ts @@ -85,18 +85,22 @@ export const PRIVATE_LOG_SIZE_IN_FIELDS = 18; export const AZTEC_MAX_EPOCH_DURATION = 48; export const GENESIS_BLOCK_HASH = 20646204262468251631976884937192820660867507115079672078981654411421834866549n; export const GENESIS_ARCHIVE_ROOT = 1002640778211850180189505934749257244705296832326768971348723156503780793518n; -export const FEE_JUICE_INITIAL_MINT = 200000000000000000000000n; export const PUBLIC_DISPATCH_SELECTOR = 3578010381; export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000; export const MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; export const MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000; export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS = 19; export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS = 12; -export const REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = 11121068431693264234253912047066709627593769337094408533543930778360n; -export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = 2889881020989534926461066592611988634597302675057895885580456197069n; -export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 24399338136397901754495080759185489776044879232766421623673792970137n; -export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 14061769416655647708490531650437236735160113654556896985372298487345n; -export const DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE = 1534834688047131268740281708431107902615560100979874281215533519862n; +export const REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = + 11121068431693264234253912047066709627593769337094408533543930778360n; +export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = + 2889881020989534926461066592611988634597302675057895885580456197069n; +export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = + 24399338136397901754495080759185489776044879232766421623673792970137n; +export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = + 14061769416655647708490531650437236735160113654556896985372298487345n; +export const DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE = + 1534834688047131268740281708431107902615560100979874281215533519862n; export const MAX_PROTOCOL_CONTRACTS = 7; export const CANONICAL_AUTH_REGISTRY_ADDRESS = 1; export const DEPLOYER_CONTRACT_ADDRESS = 2; @@ -411,4 +415,4 @@ export enum GeneratorIndex { SYMMETRIC_KEY_2 = 55, PUBLIC_TX_HASH = 56, PRIVATE_TX_HASH = 57, -} \ No newline at end of file +} diff --git a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts index fe7b41c2a56e..d40765d3a557 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts @@ -40,7 +40,7 @@ import { deployAccounts, } from '../../fixtures/snapshot_manager.js'; import { mintTokensToPrivate } from '../../fixtures/token_utils.js'; -import { type SetupOptions, setupCanonicalFeeJuice, setupSponsoredFPC } from '../../fixtures/utils.js'; +import { type SetupOptions, setupSponsoredFPC } from '../../fixtures/utils.js'; import { CrossChainTestHarness } from '../../shared/cross_chain_test_harness.js'; import { FeeJuicePortalTestingHarnessFactory, @@ -218,9 +218,7 @@ export class ClientFlowsBenchmark { async applySetupFeeJuiceSnapshot() { await this.snapshotManager.snapshot( 'setup_fee_juice', - async context => { - await setupCanonicalFeeJuice(context.pxe); - }, + async () => {}, async (_data, context) => { this.context = context; diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index 560626232a64..5d6f46bc3db0 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -39,7 +39,6 @@ import { type SetupOptions, ensureAccountsPubliclyDeployed, getBalancesFn, - setupCanonicalFeeJuice, setupSponsoredFPC, } from '../fixtures/utils.js'; import { FeeJuicePortalTestingHarnessFactory, type GasBridgingTestHarness } from '../shared/gas_portal_test_harness.js'; @@ -198,9 +197,7 @@ export class FeesTest { async applySetupFeeJuiceSnapshot() { await this.snapshotManager.snapshot( 'setup_fee_juice', - async context => { - await setupCanonicalFeeJuice(context.pxe); - }, + async () => {}, async (_data, context) => { this.context = context; diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index 126c3ef588ba..2af592688ef9 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -97,7 +97,7 @@ export class P2PNetworkTest { initialValidatorConfig.aztecProofSubmissionWindow ?? l1ContractsConfig.aztecProofSubmissionWindow, salt: 420, metricsPort: metricsPort, - numberOfInitialFundedAccounts: 1, + numberOfInitialFundedAccounts: 2, }, { aztecEpochDuration: initialValidatorConfig.aztecEpochDuration ?? l1ContractsConfig.aztecEpochDuration, diff --git a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts index a99e00069e95..50940e78a1e0 100644 --- a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts +++ b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts @@ -1,7 +1,7 @@ import type { Logger } from '@aztec/aztec.js'; import { type DeployL1ContractsArgs, type L1ContractsConfig, deployL1Contracts } from '@aztec/ethereum'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts'; +import { protocolContractTreeRoot } from '@aztec/protocol-contracts'; import type { HDAccount, PrivateKeyAccount } from 'viem'; import { foundry } from 'viem/chains'; @@ -16,7 +16,6 @@ export const setupL1Contracts = async ( L1ContractsConfig, ) => { const l1Data = await deployL1Contracts([l1RpcUrl], account, foundry, logger, { - l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, salt: undefined, diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index 427379a9742e..397dbd4c41de 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -348,7 +348,7 @@ async function setupFromFresh( const initialFundedAccounts = await generateSchnorrAccounts(numberOfInitialFundedAccounts); const sponsoredFPCAddress = await getSponsoredFPCAddress(); - const { genesisArchiveRoot, genesisBlockHash, prefilledPublicData } = await getGenesisValues( + const { genesisArchiveRoot, genesisBlockHash, prefilledPublicData, fundingNeeded } = await getGenesisValues( initialFundedAccounts.map(a => a.address).concat(sponsoredFPCAddress), opts.initialAccountFeeJuice, ); @@ -357,6 +357,7 @@ async function setupFromFresh( ...getL1ContractsConfigEnvVars(), genesisArchiveRoot, genesisBlockHash, + feeJuicePortalInitialBalance: fundingNeeded, salt: opts.salt, ...deployL1ContractsArgs, initialValidators: opts.initialValidators, diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 0afe05de5c84..f64165d65d82 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -14,7 +14,6 @@ import { type AztecNode, BatchCall, type ContractMethod, - FeeJuicePaymentMethod, type Logger, type PXE, SignerlessWallet, @@ -30,7 +29,7 @@ import { SponsoredFeePaymentMethod } from '@aztec/aztec.js/fee/testing'; import { AnvilTestWatcher, CheatCodes } from '@aztec/aztec.js/testing'; import { createBlobSinkClient } from '@aztec/blob-sink/client'; import { type BlobSinkServer, createBlobSinkServer } from '@aztec/blob-sink/server'; -import { FEE_JUICE_INITIAL_MINT, GENESIS_ARCHIVE_ROOT, GENESIS_BLOCK_HASH, SPONSORED_FPC_SALT } from '@aztec/constants'; +import { GENESIS_ARCHIVE_ROOT, GENESIS_BLOCK_HASH, SPONSORED_FPC_SALT } from '@aztec/constants'; import { DefaultMultiCallEntrypoint } from '@aztec/entrypoints/multicall'; import { type DeployL1ContractsArgs, @@ -49,10 +48,9 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { retryUntil } from '@aztec/foundation/retry'; import { TestDateProvider } from '@aztec/foundation/timer'; -import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; import { SponsoredFPCContract } from '@aztec/noir-contracts.js/SponsoredFPC'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts'; +import { protocolContractTreeRoot } from '@aztec/protocol-contracts'; import { type ProverNode, type ProverNodeConfig, createProverNode } from '@aztec/prover-node'; import { type PXEService, @@ -65,7 +63,6 @@ import type { TestSequencerClient } from '@aztec/sequencer-client/test'; import { WASMSimulator } from '@aztec/simulator/client'; import { SimulationProviderRecorderWrapper } from '@aztec/simulator/testing'; import { getContractClassFromArtifact, getContractInstanceFromDeployParams } from '@aztec/stdlib/contract'; -import { Gas } from '@aztec/stdlib/gas'; import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees'; import { @@ -82,7 +79,6 @@ import fs from 'fs/promises'; import getPort from 'get-port'; import { tmpdir } from 'os'; import * as path from 'path'; -import { inspect } from 'util'; import { type Chain, type HDAccount, type Hex, type PrivateKeyAccount, getContract } from 'viem'; import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; @@ -126,7 +122,6 @@ export const setupL1Contracts = async ( chain: Chain = foundry, ) => { const l1Data = await deployL1Contracts(l1RpcUrls, account, chain, logger, { - l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, genesisArchiveRoot: args.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT), @@ -239,8 +234,6 @@ async function setupWithRemoteEnvironment( const cheatCodes = await CheatCodes.create(config.l1RpcUrls, pxeClient!); const teardown = () => Promise.resolve(); - await setupCanonicalFeeJuice(pxeClient); - logger.verbose('Constructing available wallets from already registered accounts...'); const initialFundedAccounts = await getDeployedTestAccounts(pxeClient); const wallets = await getDeployedTestAccountsWallets(pxeClient); @@ -432,7 +425,7 @@ export async function setup( const initialFundedAccounts = opts.initialFundedAccounts ?? (await generateSchnorrAccounts(opts.numberOfInitialFundedAccounts ?? numberOfAccounts)); - const { genesisBlockHash, genesisArchiveRoot, prefilledPublicData } = await getGenesisValues( + const { genesisBlockHash, genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues( initialFundedAccounts.map(a => a.address), opts.initialAccountFeeJuice, opts.genesisPublicData, @@ -444,7 +437,7 @@ export async function setup( config.l1RpcUrls, publisherHdAccount!, logger, - { ...opts, genesisArchiveRoot, genesisBlockHash }, + { ...opts, genesisArchiveRoot, genesisBlockHash, feeJuicePortalInitialBalance: fundingNeeded }, chain, )); @@ -552,11 +545,6 @@ export async function setup( logger.verbose('Creating a pxe...'); const { pxe, teardown: pxeTeardown } = await setupPXEService(aztecNode!, pxeOpts, logger); - if (!config.skipProtocolContracts) { - logger.verbose('Setting up Fee Juice...'); - await setupCanonicalFeeJuice(pxe); - } - const accountManagers = await deployFundedSchnorrAccounts(pxe, initialFundedAccounts.slice(0, numberOfAccounts)); const wallets = await Promise.all(accountManagers.map(account => account.getWallet())); if (initialFundedAccounts.length < numberOfAccounts) { @@ -743,27 +731,6 @@ export async function expectMappingDelta( expect(diffs).toEqual(expectedDiffs); } -/** - * Deploy the canonical Fee Juice contract to a running instance. - */ -export async function setupCanonicalFeeJuice(pxe: PXE) { - // "deploy" the Fee Juice as it contains public functions - const feeJuicePortalAddress = (await pxe.getNodeInfo()).l1ContractAddresses.feeJuicePortalAddress; - const wallet = new SignerlessWallet(pxe); - const feeJuice = await FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, wallet); - - try { - const paymentMethod = new FeeJuicePaymentMethod(ProtocolContractAddress.FeeJuice); - await feeJuice.methods - .initialize(feeJuicePortalAddress, FEE_JUICE_INITIAL_MINT) - .send({ fee: { paymentMethod, gasSettings: { teardownGasLimits: Gas.empty() } } }) - .wait(); - getLogger().info(`Fee Juice successfully setup. Portal address: ${feeJuicePortalAddress}`); - } catch (error) { - getLogger().warn(`Fee Juice might have already been setup. Got error: ${inspect(error)}.`); - } -} - /** * Computes the address of the "canonical" SponosoredFPCContract. This is not a protocol contract * but by conventions its address is computed with a salt of 0. diff --git a/yarn-project/end-to-end/src/spartan/upgrade_rollup_version.test.ts b/yarn-project/end-to-end/src/spartan/upgrade_rollup_version.test.ts index 356a7e4dd1f8..7d1d6a11a891 100644 --- a/yarn-project/end-to-end/src/spartan/upgrade_rollup_version.test.ts +++ b/yarn-project/end-to-end/src/spartan/upgrade_rollup_version.test.ts @@ -11,7 +11,7 @@ import { } from '@aztec/ethereum'; import { createLogger } from '@aztec/foundation/log'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts'; +import { protocolContractTreeRoot } from '@aztec/protocol-contracts'; import { getGenesisValues } from '@aztec/world-state/testing'; import type { ChildProcess } from 'child_process'; @@ -82,7 +82,9 @@ describe('spartan_upgrade_rollup_version', () => { debugLogger.info(`l1WalletClient: ${l1WalletClient.account.address}`); const initialTestAccounts = await getInitialTestAccounts(); - const { genesisBlockHash, genesisArchiveRoot } = await getGenesisValues(initialTestAccounts.map(a => a.address)); + const { genesisBlockHash, genesisArchiveRoot, fundingNeeded } = await getGenesisValues( + initialTestAccounts.map(a => a.address), + ); const rollup = new RollupContract(l1PublicClient, originalL1ContractAddresses.rollupAddress.toString()); const { rollup: newRollup } = await deployRollupForUpgrade( @@ -94,7 +96,6 @@ describe('spartan_upgrade_rollup_version', () => { salt: Math.floor(Math.random() * 1000000), vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, - l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), genesisArchiveRoot, genesisBlockHash, ethereumSlotDuration: 12, @@ -109,6 +110,7 @@ describe('spartan_upgrade_rollup_version', () => { governanceProposerRoundSize: 10, manaTarget: BigInt(100e6), provingCostPerMana: BigInt(100), + feeJuicePortalInitialBalance: fundingNeeded, }, originalL1ContractAddresses.registryAddress, debugLogger, diff --git a/yarn-project/ethereum/src/contracts/fee_asset_handler.test.ts b/yarn-project/ethereum/src/contracts/fee_asset_handler.test.ts index de006a7ffa67..6438f8e7d4a6 100644 --- a/yarn-project/ethereum/src/contracts/fee_asset_handler.test.ts +++ b/yarn-project/ethereum/src/contracts/fee_asset_handler.test.ts @@ -33,7 +33,6 @@ describe('FeeAssetHandler', () => { privateKey = privateKeyToAccount('0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'); const vkTreeRoot = Fr.random(); const protocolContractTreeRoot = Fr.random(); - const l2FeeJuiceAddress = Fr.random(); ({ anvil, rpcUrl } = await startAnvil()); @@ -44,7 +43,6 @@ describe('FeeAssetHandler', () => { salt: originalVersionSalt, vkTreeRoot, protocolContractTreeRoot, - l2FeeJuiceAddress, genesisArchiveRoot: Fr.random(), genesisBlockHash: Fr.random(), }); diff --git a/yarn-project/ethereum/src/contracts/forwarder.test.ts b/yarn-project/ethereum/src/contracts/forwarder.test.ts index 7e005fa6a6c5..b79c9329cbec 100644 --- a/yarn-project/ethereum/src/contracts/forwarder.test.ts +++ b/yarn-project/ethereum/src/contracts/forwarder.test.ts @@ -26,7 +26,6 @@ describe('Forwarder', () => { let vkTreeRoot: Fr; let protocolContractTreeRoot: Fr; - let l2FeeJuiceAddress: Fr; let walletClient: ViemWalletClient; let publicClient: ViemPublicClient; let forwarder: ForwarderContract; @@ -40,8 +39,6 @@ describe('Forwarder', () => { privateKey = privateKeyToAccount('0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'); vkTreeRoot = Fr.random(); protocolContractTreeRoot = Fr.random(); - // Valid AztecAddress represented by its xCoord as a Fr - l2FeeJuiceAddress = Fr.fromHexString('0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f'); ({ anvil, rpcUrl } = await startAnvil()); @@ -52,7 +49,6 @@ describe('Forwarder', () => { salt: undefined, vkTreeRoot, protocolContractTreeRoot, - l2FeeJuiceAddress, genesisArchiveRoot: Fr.random(), genesisBlockHash: Fr.random(), }); diff --git a/yarn-project/ethereum/src/contracts/governance.test.ts b/yarn-project/ethereum/src/contracts/governance.test.ts index a6722044f9f3..06af662861be 100644 --- a/yarn-project/ethereum/src/contracts/governance.test.ts +++ b/yarn-project/ethereum/src/contracts/governance.test.ts @@ -19,7 +19,6 @@ describe('Governance', () => { let vkTreeRoot: Fr; let protocolContractTreeRoot: Fr; - let l2FeeJuiceAddress: Fr; let walletClient: ViemWalletClient; let publicClient: ViemPublicClient; let governance: GovernanceContract; @@ -29,8 +28,6 @@ describe('Governance', () => { privateKey = privateKeyToAccount('0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'); vkTreeRoot = Fr.random(); protocolContractTreeRoot = Fr.random(); - // Valid AztecAddress represented by its xCoord as a Fr - l2FeeJuiceAddress = Fr.fromHexString('0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f'); ({ anvil, rpcUrl } = await startAnvil()); @@ -41,7 +38,6 @@ describe('Governance', () => { salt: undefined, vkTreeRoot, protocolContractTreeRoot, - l2FeeJuiceAddress, genesisArchiveRoot: Fr.random(), genesisBlockHash: Fr.random(), }); diff --git a/yarn-project/ethereum/src/contracts/registry.test.ts b/yarn-project/ethereum/src/contracts/registry.test.ts index a1016aa8f509..282859183250 100644 --- a/yarn-project/ethereum/src/contracts/registry.test.ts +++ b/yarn-project/ethereum/src/contracts/registry.test.ts @@ -25,7 +25,6 @@ describe('Registry', () => { let vkTreeRoot: Fr; let protocolContractTreeRoot: Fr; - let l2FeeJuiceAddress: Fr; let publicClient: L1Clients['publicClient']; let walletClient: L1Clients['walletClient']; let registry: RegistryContract; @@ -38,7 +37,6 @@ describe('Registry', () => { privateKey = privateKeyToAccount('0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'); vkTreeRoot = Fr.random(); protocolContractTreeRoot = Fr.random(); - l2FeeJuiceAddress = Fr.random(); ({ anvil, rpcUrl } = await startAnvil()); @@ -49,7 +47,6 @@ describe('Registry', () => { salt: originalVersionSalt, vkTreeRoot, protocolContractTreeRoot, - l2FeeJuiceAddress, genesisArchiveRoot: Fr.random(), genesisBlockHash: Fr.random(), }); @@ -124,7 +121,6 @@ describe('Registry', () => { salt: newVersionSalt, vkTreeRoot, protocolContractTreeRoot, - l2FeeJuiceAddress, genesisArchiveRoot: Fr.random(), genesisBlockHash: Fr.random(), }, diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.test.ts b/yarn-project/ethereum/src/deploy_l1_contracts.test.ts index aea6213de737..738a3768d62f 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.test.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.test.ts @@ -20,7 +20,6 @@ describe('deploy_l1_contracts', () => { let genesisArchiveRoot: Fr; let genesisBlockHash: Fr; let initialValidators: EthAddress[]; - let l2FeeJuiceAddress: Fr; // Use these environment variables to run against a live node. Eg to test against spartan's eth-devnet: // BLOCK_TIME=1 spartan/aztec-network/eth-devnet/run-locally.sh @@ -37,8 +36,6 @@ describe('deploy_l1_contracts', () => { genesisArchiveRoot = Fr.random(); genesisBlockHash = Fr.random(); initialValidators = times(3, EthAddress.random); - // Valid AztecAddress represented by its xCoord as a Fr - l2FeeJuiceAddress = Fr.fromHexString('0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f'); if (!rpcUrl) { ({ stop, rpcUrl } = await startAnvil()); @@ -63,7 +60,6 @@ describe('deploy_l1_contracts', () => { protocolContractTreeRoot, genesisArchiveRoot, genesisBlockHash, - l2FeeJuiceAddress, l1TxConfig: { checkIntervalMs: 100 }, ...args, }); diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index b4f0821916bc..3c7b93697d56 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -203,12 +203,6 @@ export const l1Artifacts = { }; export interface DeployL1ContractsArgs extends L1ContractsConfig { - /** - * The address of the L2 Fee Juice contract. - * It should be an AztecAddress, but the type is defined in stdlib, - * which would create a circular import - * */ - l2FeeJuiceAddress: Fr; /** The vk tree root. */ vkTreeRoot: Fr; /** The protocol contract tree root. */ @@ -225,6 +219,8 @@ export interface DeployL1ContractsArgs extends L1ContractsConfig { l1TxConfig?: Partial; /** Enable fast mode for deployments (fire and forget transactions) */ acceleratedTestDeployments?: boolean; + /** The initial balance of the fee juice portal. This is the amount of fee juice that is prefunded to accounts */ + feeJuicePortalInitialBalance?: bigint; } /** @@ -539,64 +535,24 @@ export const deployRollup = async ( ); } - const feeJuicePortalAddress = await rollupContract.getFeeJuicePortal(); - - // @note This value MUST match what is in `constants.nr`. It is currently specified here instead of just importing - // because there is circular dependency hell. This is a temporary solution. #3342 - // @todo #8084 - // fund the portal contract with Fee Juice - const FEE_JUICE_INITIAL_MINT = 200000000000000000000000n; - - // In fast mode, use the L1TxUtils to send transactions with nonce management - const { txHash: mintTxHash } = await deployer.sendTransaction({ - to: addresses.feeJuiceAddress.toString(), - data: encodeFunctionData({ - abi: l1Artifacts.feeAsset.contractAbi, - functionName: 'mint', - args: [feeJuicePortalAddress.toString(), FEE_JUICE_INITIAL_MINT], - }), - }); - logger.verbose(`Funding fee juice portal contract with fee juice in ${mintTxHash} (accelerated test deployments)`); - txHashes.push(mintTxHash); - - // @note This is used to ensure we fully wait for the transaction when running against a real chain - // otherwise we execute subsequent transactions too soon - if (!args.acceleratedTestDeployments) { - await clients.publicClient.waitForTransactionReceipt({ hash: mintTxHash }); - logger.verbose(`Funding fee juice portal contract with fee juice in ${mintTxHash}`); - } + if (args.feeJuicePortalInitialBalance && args.feeJuicePortalInitialBalance > 0n) { + const feeJuicePortalAddress = await rollupContract.getFeeJuicePortal(); - const feeJuicePortal = getContract({ - address: getAddress(feeJuicePortalAddress.toString()), - abi: l1Artifacts.feeJuicePortal.contractAbi, - client: clients.publicClient, - }); - - // Check if portal needs initialization - let needsInitialization = args.acceleratedTestDeployments; - if (!args.acceleratedTestDeployments) { - // Only check if not in fast mode and not already known to need initialization - needsInitialization = !(await feeJuicePortal.read.initialized()); - } - if (needsInitialization) { - const { txHash: initPortalTxHash } = await deployer.sendTransaction({ - to: feeJuicePortalAddress.toString(), + // In fast mode, use the L1TxUtils to send transactions with nonce management + const { txHash: mintTxHash } = await deployer.sendTransaction({ + to: addresses.feeJuiceAddress.toString(), data: encodeFunctionData({ - abi: l1Artifacts.feeJuicePortal.contractAbi, - functionName: 'initialize', - args: [], + abi: l1Artifacts.feeAsset.contractAbi, + functionName: 'mint', + args: [feeJuicePortalAddress.toString(), args.feeJuicePortalInitialBalance], }), }); - txHashes.push(initPortalTxHash); - logger.verbose(`Fee juice portal initializing in tx ${initPortalTxHash}`); - } else { - logger.verbose(`Fee juice portal is already initialized`); + logger.verbose( + `Funding fee juice portal with ${args.feeJuicePortalInitialBalance} fee juice in ${mintTxHash} (accelerated test deployments)`, + ); + txHashes.push(mintTxHash); } - logger.verbose( - `Initialized Fee Juice Portal at ${feeJuicePortalAddress} to bridge between L1 ${addresses.feeJuiceAddress} to L2 ${args.l2FeeJuiceAddress}`, - ); - const slashFactoryAddress = await deployer.deploy(l1Artifacts.slashFactory, [rollupAddress.toString()]); logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`); diff --git a/yarn-project/world-state/src/testing.ts b/yarn-project/world-state/src/testing.ts index b5836dc97ca7..36d3068a2371 100644 --- a/yarn-project/world-state/src/testing.ts +++ b/yarn-project/world-state/src/testing.ts @@ -56,5 +56,6 @@ export async function getGenesisValues( genesisArchiveRoot, genesisBlockHash, prefilledPublicData, + fundingNeeded: BigInt(initialAccounts.length) * initialAccountFeeJuice.toBigInt(), }; }