From 15c7a4d99ac35b509a72daa7b52ce21e78e9399e Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:32:15 -0300 Subject: [PATCH 1/6] fix: query l1 withdrawer instead of calculating it * fix: remove mainnet from op url name since it can be testnet as well --- .../RevSharePostTaskAssertions.t.sol | 104 ++++++++++-------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/test/integration/RevSharePostTaskAssertions.t.sol b/test/integration/RevSharePostTaskAssertions.t.sol index c279efc2b..1fbeb7bdb 100644 --- a/test/integration/RevSharePostTaskAssertions.t.sol +++ b/test/integration/RevSharePostTaskAssertions.t.sol @@ -2,32 +2,36 @@ pragma solidity 0.8.15; import {IntegrationBase} from "./IntegrationBase.t.sol"; +import {IFeeSplitter} from "src/interfaces/IFeeSplitter.sol"; +import {ISuperchainRevSharesCalculator} from "src/interfaces/ISuperchainRevSharesCalculator.sol"; /// @title RevSharePostTaskAssertionsTest /// @notice Integration test for asserting Rev Share contract state after task execution. /// This test does NOT execute the task simulation or relay L1->L2 messages. /// It directly asserts the expected state on L2 chains after a real task execution. +/// The L1Withdrawer and calculator addresses are queried directly from the FeeSplitter +/// on-chain, making this test compatible with any deployment mechanism (CREATE2 or genesis). /// @dev Required environment variables: /// - RPC_URL: L2 RPC URL to create fork /// - L1_RPC_URL: L1 RPC URL to create fork (for withdrawal relay tests) -/// - OP_MAINNET_RPC_URL: OP Mainnet L2 RPC URL (for L1→L2 relay tests, defaults to RPC_URL) +/// - OP_RPC_URL: OP L2 RPC URL for L1→L2 relay tests (defaults to RPC_URL) /// - OPTIMISM_PORTAL: Portal address for the chain /// - L1_MESSENGER: L1CrossDomainMessenger address for the chain -/// - MIN_WITHDRAWAL_AMOUNT: Min withdrawal amount for L1Withdrawer -/// - L1_WITHDRAWAL_RECIPIENT: L1 withdrawal recipient address -/// - WITHDRAWAL_GAS_LIMIT: Gas limit for withdrawals -/// - CHAIN_FEES_RECIPIENT: Chain fees recipient address +/// - MIN_WITHDRAWAL_AMOUNT: Expected min withdrawal amount for L1Withdrawer (wei) +/// - L1_WITHDRAWAL_RECIPIENT: Expected L1 withdrawal recipient address +/// - WITHDRAWAL_GAS_LIMIT: Expected gas limit for withdrawals +/// - CHAIN_FEES_RECIPIENT: Expected chain fees recipient address /// @dev Example command: /// ```sh -/// RPC_URL="https://mainnet.optimism.io" \ -/// L1_RPC_URL="https://eth.llamarpc.com" \ -/// OP_MAINNET_RPC_URL="https://mainnet.optimism.io" \ -/// OPTIMISM_PORTAL="0xbEb5Fc579115071764c7423A4f12eDde41f106Ed" \ -/// L1_MESSENGER="0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1" \ -/// MIN_WITHDRAWAL_AMOUNT="2000000000000000000" \ -/// L1_WITHDRAWAL_RECIPIENT="0xed9B99a703BaD32AC96FDdc313c0652e379251Fd" \ -/// WITHDRAWAL_GAS_LIMIT="800000" \ -/// CHAIN_FEES_RECIPIENT="0x16A27462B4D61BDD72CbBabd3E43e11791F7A28c" \ +/// RPC_URL="https://revshare-alpha-0.optimism.io" \ +/// L1_RPC_URL="https://ethereum-sepolia-rpc.publicnode.com" \ +/// OP_RPC_URL="https://sepolia.optimism.io" \ +/// OPTIMISM_PORTAL="0x5b03d83e3355cdb33fa89bafc598128c2992e0ac" \ +/// L1_MESSENGER="0x5bb384968c190f6452b8db4f6ba8a282005947b3" \ +/// MIN_WITHDRAWAL_AMOUNT="10000000000000000000" \ +/// L1_WITHDRAWAL_RECIPIENT="0x81c01427DFA9A2512b4EBf1462868856BA4aA91a" \ +/// WITHDRAWAL_GAS_LIMIT="1000000" \ +/// CHAIN_FEES_RECIPIENT="0x455A1115C97cb0E2b24B064C00a9E13872cC37ca" \ /// forge test --match-contract RevSharePostTaskAssertionsTest /// ``` contract RevSharePostTaskAssertionsTest is IntegrationBase { @@ -37,10 +41,16 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { // Chain configuration from env vars address internal _portal; address internal _l1Messenger; - uint256 internal _minWithdrawalAmount; - address internal _l1WithdrawalRecipient; - uint32 internal _withdrawalGasLimit; - address internal _chainFeesRecipient; + + // Expected values from env vars + uint256 internal _expectedMinWithdrawalAmount; + address internal _expectedL1WithdrawalRecipient; + uint32 internal _expectedWithdrawalGasLimit; + address internal _expectedChainFeesRecipient; + + // RevShare addresses discovered from on-chain state + address internal _calculator; + address internal _l1Withdrawer; // Flag to track if env vars are set bool internal _isEnabled; @@ -57,30 +67,40 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { // Read env vars with defaults to detect if they're set string memory rpcUrl = vm.envOr("RPC_URL", string("")); string memory l1RpcUrl = vm.envOr("L1_RPC_URL", string("")); - string memory opMainnetRpcUrl = vm.envOr("OP_MAINNET_RPC_URL", rpcUrl); // Defaults to RPC_URL + string memory opRpcUrl = vm.envOr("OP_RPC_URL", rpcUrl); // Defaults to RPC_URL _portal = vm.envOr("OPTIMISM_PORTAL", address(0)); _l1Messenger = vm.envOr("L1_MESSENGER", address(0)); - _minWithdrawalAmount = vm.envOr("MIN_WITHDRAWAL_AMOUNT", uint256(0)); - _l1WithdrawalRecipient = vm.envOr("L1_WITHDRAWAL_RECIPIENT", address(0)); - _withdrawalGasLimit = uint32(vm.envOr("WITHDRAWAL_GAS_LIMIT", uint256(0))); - _chainFeesRecipient = vm.envOr("CHAIN_FEES_RECIPIENT", address(0)); + + // Expected values to verify against on-chain state + _expectedMinWithdrawalAmount = vm.envOr("MIN_WITHDRAWAL_AMOUNT", uint256(0)); + _expectedL1WithdrawalRecipient = vm.envOr("L1_WITHDRAWAL_RECIPIENT", address(0)); + _expectedWithdrawalGasLimit = uint32(vm.envOr("WITHDRAWAL_GAS_LIMIT", uint256(0))); + _expectedChainFeesRecipient = vm.envOr("CHAIN_FEES_RECIPIENT", address(0)); // Check if all required env vars are set bool hasRpcUrl = bytes(rpcUrl).length > 0; bool hasL1RpcUrl = bytes(l1RpcUrl).length > 0; bool hasPortal = _portal != address(0); bool hasL1Messenger = _l1Messenger != address(0); - bool hasL1WithdrawalRecipient = _l1WithdrawalRecipient != address(0); - bool hasWithdrawalGasLimit = _withdrawalGasLimit != 0; - bool hasChainFeesRecipient = _chainFeesRecipient != address(0); + bool hasExpectedL1WithdrawalRecipient = _expectedL1WithdrawalRecipient != address(0); + bool hasExpectedWithdrawalGasLimit = _expectedWithdrawalGasLimit != 0; + bool hasExpectedChainFeesRecipient = _expectedChainFeesRecipient != address(0); - _isEnabled = hasRpcUrl && hasL1RpcUrl && hasPortal && hasL1Messenger && hasL1WithdrawalRecipient - && hasWithdrawalGasLimit && hasChainFeesRecipient; + _isEnabled = hasRpcUrl && hasL1RpcUrl && hasPortal && hasL1Messenger && hasExpectedL1WithdrawalRecipient + && hasExpectedWithdrawalGasLimit && hasExpectedChainFeesRecipient; if (_isEnabled) { _mainnetForkId = vm.createFork(l1RpcUrl); - _opMainnetForkId = vm.createFork(opMainnetRpcUrl); + _opMainnetForkId = vm.createFork(opRpcUrl); _l2ForkId = vm.createFork(rpcUrl); + + // Query RevShare addresses from on-chain state + vm.selectFork(_l2ForkId); + _calculator = IFeeSplitter(FEE_SPLITTER).sharesCalculator(); + require(_calculator != address(0), "FeeSplitter calculator not set - RevShare not configured"); + + _l1Withdrawer = address(ISuperchainRevSharesCalculator(_calculator).shareRecipient()); + require(_l1Withdrawer != address(0), "Calculator shareRecipient not set"); } } @@ -88,17 +108,13 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { function test_assertRevShareState() public onlyIfEnabled { vm.selectFork(_l2ForkId); - address l1Withdrawer = - _computeL1WithdrawerAddress(_minWithdrawalAmount, _l1WithdrawalRecipient, _withdrawalGasLimit); - address revShareCalculator = _computeRevShareCalculatorAddress(l1Withdrawer, _chainFeesRecipient); - _assertL2State( - l1Withdrawer, - revShareCalculator, - _minWithdrawalAmount, - _l1WithdrawalRecipient, - _withdrawalGasLimit, - _chainFeesRecipient + _l1Withdrawer, + _calculator, + _expectedMinWithdrawalAmount, + _expectedL1WithdrawalRecipient, + _expectedWithdrawalGasLimit, + _expectedChainFeesRecipient ); } @@ -107,10 +123,6 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { // Fund vaults _fundVaults(1 ether, _l2ForkId); - // Compute L1Withdrawer address - address l1Withdrawer = - _computeL1WithdrawerAddress(_minWithdrawalAmount, _l1WithdrawalRecipient, _withdrawalGasLimit); - // Disburse fees and assert withdrawal // Expected L1Withdrawer share = 3 ether * 15% = 0.45 ether // It is 3 ether instead of 4 because net revenue doesn't count L1FeeVault's balance @@ -122,12 +134,12 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { _mainnetForkId, _l2ForkId, _opMainnetForkId, - l1Withdrawer, - _l1WithdrawalRecipient, + _l1Withdrawer, + _expectedL1WithdrawalRecipient, expectedWithdrawalAmount, _portal, _l1Messenger, - _withdrawalGasLimit + _expectedWithdrawalGasLimit ); } } From a0287303e825af8e9b04306b24d2e298b1615bcf Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 26 Dec 2025 17:26:57 -0300 Subject: [PATCH 2/6] refactor: improve withdrawal assertions using below and above threshold * fix: receive op cdm as input instead of hardcoding mainnet --- test/integration/IntegrationBase.t.sol | 12 ++-- ...RevShareContractsUpgraderIntegration.t.sol | 3 +- .../RevSharePostTaskAssertions.t.sol | 58 +++++++++++++++---- .../RevShareSetupIntegration.t.sol | 3 +- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index 3124ce291..12d2f18d0 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -45,9 +45,6 @@ abstract contract IntegrationBase is Test { // OP Mainnet fees recipient (OPM multisig) - target for FeesDepositor deposits address internal constant OP_MAINNET_FEES_RECIPIENT = 0x16A27462B4D61BDD72CbBabd3E43e11791F7A28c; - // Aliased address for L1→L2 message relay - address internal immutable OP_ALIASED_L1_MESSENGER = AddressAliasHelper.applyL1ToL2Alias(OP_MAINNET_L1_MESSENGER); - // Simulation flag for task execution bool internal constant IS_SIMULATE = true; @@ -335,6 +332,7 @@ abstract contract IntegrationBase is Test { /// @param _portal The OptimismPortal address for this L2 chain /// @param _l1Messenger The L1CrossDomainMessenger address for this L2 chain /// @param _withdrawalGasLimit The gas limit used for L1 withdrawals + /// @param _opL1Messenger The OP L1CrossDomainMessenger address (mainnet or sepolia) function _executeDisburseAndAssertWithdrawal( uint256 _l1ForkId, uint256 _forkId, @@ -344,7 +342,8 @@ abstract contract IntegrationBase is Test { uint256 _expectedWithdrawalAmount, address _portal, address _l1Messenger, - uint32 _withdrawalGasLimit + uint32 _withdrawalGasLimit, + address _opL1Messenger ) internal { vm.selectFork(_forkId); vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); @@ -362,7 +361,7 @@ abstract contract IntegrationBase is Test { // The 'from' address in TransactionDeposited is the aliased L1CrossDomainMessenger (not the FeesDepositor) vm.expectEmit(true, true, true, false, OP_MAINNET_PORTAL); emit TransactionDeposited( - OP_ALIASED_L1_MESSENGER, // aliased L1CrossDomainMessenger (caller of depositTransaction) + AddressAliasHelper.applyL1ToL2Alias(_opL1Messenger), // aliased L1CrossDomainMessenger L2_CROSS_DOMAIN_MESSENGER, // L2 CrossDomainMessenger 0, "" @@ -384,8 +383,9 @@ abstract contract IntegrationBase is Test { uint256 recipientBalanceBefore = OP_MAINNET_FEES_RECIPIENT.balance; // Relay the L1→L2 message (simple ETH transfer to OPM multisig) + address aliasedOpL1Messenger = AddressAliasHelper.applyL1ToL2Alias(_opL1Messenger); _relayL1ToL2Message( - OP_ALIASED_L1_MESSENGER, + aliasedOpL1Messenger, _l1WithdrawalRecipient, // sender (FeesDepositor) OP_MAINNET_FEES_RECIPIENT, // target (OPM multisig) _expectedWithdrawalAmount, diff --git a/test/integration/RevShareContractsUpgraderIntegration.t.sol b/test/integration/RevShareContractsUpgraderIntegration.t.sol index 4fb06c8f8..30aa82463 100644 --- a/test/integration/RevShareContractsUpgraderIntegration.t.sol +++ b/test/integration/RevShareContractsUpgraderIntegration.t.sol @@ -115,7 +115,8 @@ contract RevShareContractsUpgraderIntegrationTest is IntegrationBase { expectedWithdrawalAmount, chain.portal, chain.l1Messenger, - chain.withdrawalGasLimit + chain.withdrawalGasLimit, + OP_MAINNET_L1_MESSENGER ); } } diff --git a/test/integration/RevSharePostTaskAssertions.t.sol b/test/integration/RevSharePostTaskAssertions.t.sol index 1fbeb7bdb..b6b4278e6 100644 --- a/test/integration/RevSharePostTaskAssertions.t.sol +++ b/test/integration/RevSharePostTaskAssertions.t.sol @@ -17,6 +17,7 @@ import {ISuperchainRevSharesCalculator} from "src/interfaces/ISuperchainRevShare /// - OP_RPC_URL: OP L2 RPC URL for L1→L2 relay tests (defaults to RPC_URL) /// - OPTIMISM_PORTAL: Portal address for the chain /// - L1_MESSENGER: L1CrossDomainMessenger address for the chain +/// - OP_L1_MESSENGER: OP L1CrossDomainMessenger address (defaults to mainnet) /// - MIN_WITHDRAWAL_AMOUNT: Expected min withdrawal amount for L1Withdrawer (wei) /// - L1_WITHDRAWAL_RECIPIENT: Expected L1 withdrawal recipient address /// - WITHDRAWAL_GAS_LIMIT: Expected gas limit for withdrawals @@ -28,6 +29,7 @@ import {ISuperchainRevSharesCalculator} from "src/interfaces/ISuperchainRevShare /// OP_RPC_URL="https://sepolia.optimism.io" \ /// OPTIMISM_PORTAL="0x5b03d83e3355cdb33fa89bafc598128c2992e0ac" \ /// L1_MESSENGER="0x5bb384968c190f6452b8db4f6ba8a282005947b3" \ +/// OP_L1_MESSENGER="0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef" \ /// MIN_WITHDRAWAL_AMOUNT="10000000000000000000" \ /// L1_WITHDRAWAL_RECIPIENT="0x81c01427DFA9A2512b4EBf1462868856BA4aA91a" \ /// WITHDRAWAL_GAS_LIMIT="1000000" \ @@ -41,6 +43,7 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { // Chain configuration from env vars address internal _portal; address internal _l1Messenger; + address internal _opL1Messenger; // Expected values from env vars uint256 internal _expectedMinWithdrawalAmount; @@ -70,6 +73,7 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { string memory opRpcUrl = vm.envOr("OP_RPC_URL", rpcUrl); // Defaults to RPC_URL _portal = vm.envOr("OPTIMISM_PORTAL", address(0)); _l1Messenger = vm.envOr("L1_MESSENGER", address(0)); + _opL1Messenger = vm.envOr("OP_L1_MESSENGER", OP_MAINNET_L1_MESSENGER); // Defaults to mainnet // Expected values to verify against on-chain state _expectedMinWithdrawalAmount = vm.envOr("MIN_WITHDRAWAL_AMOUNT", uint256(0)); @@ -118,17 +122,50 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { ); } - /// @notice Test the withdrawal flow on the L2 chain + /// @notice Test the withdrawal flow on the L2 chain - tests both below and above threshold paths + // Fund vaults with half the minWithdrawalAmount so that: + // - First disburse: share = minWithdrawalAmount / 2 (below threshold, no withdrawal) + // - Second disburse: total = minWithdrawalAmount (at threshold, triggers withdrawal) function test_withdrawalFlow() public onlyIfEnabled { - // Fund vaults - _fundVaults(1 ether, _l2ForkId); + // L1Withdrawer share = netRevenue * 15% = vaultFunding * 3 * 15 / 100 = vaultFunding * 45 / 100 + // To get share = minWithdrawalAmount / 2, we need vaultFunding = minWithdrawalAmount / 2 * 100 / 45 + uint256 sharePerDisburse = _expectedMinWithdrawalAmount / 2; + uint256 vaultFunding = (sharePerDisburse * 100) / 45; - // Disburse fees and assert withdrawal - // Expected L1Withdrawer share = 3 ether * 15% = 0.45 ether - // It is 3 ether instead of 4 because net revenue doesn't count L1FeeVault's balance - // For details on the rev share calculation, check the SuperchainRevSharesCalculator contract. - // https://github.com/ethereum-optimism/optimism/blob/f392d4b7e8bc5d1c8d38fcf19c8848764f8bee3b/packages/contracts-bedrock/src/L2/SuperchainRevSharesCalculator.sol#L67-L101 - uint256 expectedWithdrawalAmount = 0.45 ether; + // ==================== PART 1: Below threshold - no withdrawal ==================== + vm.selectFork(_l2ForkId); + + // Fund vaults for first disburse + _fundVaults(vaultFunding, _l2ForkId); + + // Warp time to allow disbursement + vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); + + // Record L1Withdrawer balance before + uint256 l1WithdrawerBalanceBefore = _l1Withdrawer.balance; + + // Disburse fees - should NOT trigger withdrawal (below threshold) + IFeeSplitter(FEE_SPLITTER).disburseFees(); + + // Verify funds accumulated in L1Withdrawer (no withdrawal triggered) + uint256 l1WithdrawerBalanceAfter = _l1Withdrawer.balance; + assertEq( + l1WithdrawerBalanceAfter - l1WithdrawerBalanceBefore, + sharePerDisburse, + "L1Withdrawer should have half of threshold" + ); + + // ==================== PART 2: At threshold - withdrawal triggers ==================== + + // Fund vaults again for second disburse + _fundVaults(vaultFunding, _l2ForkId); + + // Warp time again + vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); + + // Now the total in L1Withdrawer will be: previous balance + new share = minWithdrawalAmount + uint256 expectedWithdrawalAmount = sharePerDisburse * 2; + assertEq(expectedWithdrawalAmount, _expectedMinWithdrawalAmount, "Total should equal threshold"); _executeDisburseAndAssertWithdrawal( _mainnetForkId, @@ -139,7 +176,8 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { expectedWithdrawalAmount, _portal, _l1Messenger, - _expectedWithdrawalGasLimit + _expectedWithdrawalGasLimit, + _opL1Messenger ); } } diff --git a/test/integration/RevShareSetupIntegration.t.sol b/test/integration/RevShareSetupIntegration.t.sol index 80b9424df..0d6e20e84 100644 --- a/test/integration/RevShareSetupIntegration.t.sol +++ b/test/integration/RevShareSetupIntegration.t.sol @@ -228,7 +228,8 @@ contract RevShareSetupIntegrationTest is IntegrationBase { expectedWithdrawalAmount, chain.portal, chain.l1Messenger, - chain.withdrawalGasLimit + chain.withdrawalGasLimit, + OP_MAINNET_L1_MESSENGER ); } } From 36a5392843c0fcfa47c6a13157b412c2e20ad133 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 26 Dec 2025 18:47:38 -0300 Subject: [PATCH 3/6] fix: rounding error on assertion --- .../RevSharePostTaskAssertions.t.sol | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/test/integration/RevSharePostTaskAssertions.t.sol b/test/integration/RevSharePostTaskAssertions.t.sol index b6b4278e6..988828033 100644 --- a/test/integration/RevSharePostTaskAssertions.t.sol +++ b/test/integration/RevSharePostTaskAssertions.t.sol @@ -123,20 +123,17 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { } /// @notice Test the withdrawal flow on the L2 chain - tests both below and above threshold paths - // Fund vaults with half the minWithdrawalAmount so that: - // - First disburse: share = minWithdrawalAmount / 2 (below threshold, no withdrawal) - // - Second disburse: total = minWithdrawalAmount (at threshold, triggers withdrawal) + // Fund vaults so that: + // - First disburse: share < minWithdrawalAmount (below threshold, no withdrawal) + // - Second disburse: total >= minWithdrawalAmount (triggers withdrawal) function test_withdrawalFlow() public onlyIfEnabled { - // L1Withdrawer share = netRevenue * 15% = vaultFunding * 3 * 15 / 100 = vaultFunding * 45 / 100 - // To get share = minWithdrawalAmount / 2, we need vaultFunding = minWithdrawalAmount / 2 * 100 / 45 - uint256 sharePerDisburse = _expectedMinWithdrawalAmount / 2; - uint256 vaultFunding = (sharePerDisburse * 100) / 45; - // ==================== PART 1: Below threshold - no withdrawal ==================== vm.selectFork(_l2ForkId); - // Fund vaults for first disburse - _fundVaults(vaultFunding, _l2ForkId); + // Fund vaults to get ~half threshold as share + // L1Withdrawer share = netRevenue * 15% = vaultFunding * 3 * 15 / 100 = vaultFunding * 45 / 100 + uint256 firstVaultFunding = (_expectedMinWithdrawalAmount * 100) / 90; + _fundVaults(firstVaultFunding, _l2ForkId); // Warp time to allow disbursement vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); @@ -149,23 +146,29 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { // Verify funds accumulated in L1Withdrawer (no withdrawal triggered) uint256 l1WithdrawerBalanceAfter = _l1Withdrawer.balance; + uint256 expectedFirstShare = (firstVaultFunding * 3 * 15) / 100; assertEq( l1WithdrawerBalanceAfter - l1WithdrawerBalanceBefore, - sharePerDisburse, - "L1Withdrawer should have half of threshold" + expectedFirstShare, + "L1Withdrawer should have received expected share" ); // ==================== PART 2: At threshold - withdrawal triggers ==================== - // Fund vaults again for second disburse - _fundVaults(vaultFunding, _l2ForkId); + // Calculate how much more we need to reach the threshold + uint256 remainingToThreshold = _expectedMinWithdrawalAmount - l1WithdrawerBalanceAfter; + // Fund vaults to get at least the remaining amount as share + // share = vaultFunding * 45 / 100, so vaultFunding = share * 100 / 45 + // Round up to ensure we exceed threshold: (a + b - 1) / b + uint256 secondVaultFunding = ((remainingToThreshold * 100) + 44) / 45; + _fundVaults(secondVaultFunding, _l2ForkId); // Warp time again vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); - // Now the total in L1Withdrawer will be: previous balance + new share = minWithdrawalAmount - uint256 expectedWithdrawalAmount = sharePerDisburse * 2; - assertEq(expectedWithdrawalAmount, _expectedMinWithdrawalAmount, "Total should equal threshold"); + // Calculate expected withdrawal amount (current balance + new share) + uint256 secondShare = (secondVaultFunding * 45) / 100; + uint256 expectedWithdrawalAmount = l1WithdrawerBalanceAfter + secondShare; _executeDisburseAndAssertWithdrawal( _mainnetForkId, From 8b99eb65f8f5081f8e202943cc10161bd886494d Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:19:06 -0300 Subject: [PATCH 4/6] chore: replace mainnet hardcoded values for env inputs --- test/integration/IntegrationBase.t.sol | 24 +++++++++++-------- ...RevShareContractsUpgraderIntegration.t.sol | 4 +++- .../RevSharePostTaskAssertions.t.sol | 8 ++++++- .../RevShareSetupIntegration.t.sol | 4 +++- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index 12d2f18d0..b75fa5b8e 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -325,7 +325,7 @@ abstract contract IntegrationBase is Test { /// @notice Execute disburseFees and assert that it triggers a withdrawal with the expected amount /// @param _l1ForkId The L1 fork ID /// @param _forkId The fork ID of the L2 chain to test - /// @param _opL2ForkId The OP Mainnet L2 fork ID (for relaying L1→L2 deposits) + /// @param _opL2ForkId The OP L2 fork ID (for relaying L1→L2 deposits from FeesDepositor) /// @param _l1Withdrawer The L1Withdrawer address that emits the WithdrawalInitiated event /// @param _l1WithdrawalRecipient The expected recipient of the withdrawal /// @param _expectedWithdrawalAmount The expected withdrawal amount @@ -333,6 +333,8 @@ abstract contract IntegrationBase is Test { /// @param _l1Messenger The L1CrossDomainMessenger address for this L2 chain /// @param _withdrawalGasLimit The gas limit used for L1 withdrawals /// @param _opL1Messenger The OP L1CrossDomainMessenger address (mainnet or sepolia) + /// @param _opPortal The OP Portal address (mainnet or sepolia) - where FeesDepositor deposits + /// @param _opFeesRecipient The OP fees recipient address (mainnet or sepolia) function _executeDisburseAndAssertWithdrawal( uint256 _l1ForkId, uint256 _forkId, @@ -343,7 +345,9 @@ abstract contract IntegrationBase is Test { address _portal, address _l1Messenger, uint32 _withdrawalGasLimit, - address _opL1Messenger + address _opL1Messenger, + address _opPortal, + address _opFeesRecipient ) internal { vm.selectFork(_forkId); vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); @@ -356,10 +360,10 @@ abstract contract IntegrationBase is Test { vm.selectFork(_l1ForkId); if (_expectedWithdrawalAmount >= FEES_DEPOSITOR_THRESHOLD) { - // Expect TransactionDeposited event from OP Mainnet Portal + // Expect TransactionDeposited event from OP Portal // Note: FeesDepositor calls L1CrossDomainMessenger.sendMessage(), which calls OptimismPortal.depositTransaction() // The 'from' address in TransactionDeposited is the aliased L1CrossDomainMessenger (not the FeesDepositor) - vm.expectEmit(true, true, true, false, OP_MAINNET_PORTAL); + vm.expectEmit(true, true, true, false, _opPortal); emit TransactionDeposited( AddressAliasHelper.applyL1ToL2Alias(_opL1Messenger), // aliased L1CrossDomainMessenger L2_CROSS_DOMAIN_MESSENGER, // L2 CrossDomainMessenger @@ -377,27 +381,27 @@ abstract contract IntegrationBase is Test { "" // data (empty for ETH transfer) ); - // Now relay the deposit from L1 to OP Mainnet L2 + // Now relay the deposit from L1 to OP L2 vm.selectFork(_opL2ForkId); - uint256 recipientBalanceBefore = OP_MAINNET_FEES_RECIPIENT.balance; + uint256 recipientBalanceBefore = _opFeesRecipient.balance; - // Relay the L1→L2 message (simple ETH transfer to OPM multisig) + // Relay the L1→L2 message (simple ETH transfer to fees recipient) address aliasedOpL1Messenger = AddressAliasHelper.applyL1ToL2Alias(_opL1Messenger); _relayL1ToL2Message( aliasedOpL1Messenger, _l1WithdrawalRecipient, // sender (FeesDepositor) - OP_MAINNET_FEES_RECIPIENT, // target (OPM multisig) + _opFeesRecipient, // target (fees recipient) _expectedWithdrawalAmount, 200_000, // gas limit for simple ETH transfer "" // empty data for ETH transfer ); - uint256 recipientBalanceAfter = OP_MAINNET_FEES_RECIPIENT.balance; + uint256 recipientBalanceAfter = _opFeesRecipient.balance; assertEq( recipientBalanceAfter - recipientBalanceBefore, _expectedWithdrawalAmount, - "OP Mainnet fees recipient should receive the withdrawal amount" + "OP fees recipient should receive the withdrawal amount" ); } else { // FeesDepositor holds the ETH (below threshold) diff --git a/test/integration/RevShareContractsUpgraderIntegration.t.sol b/test/integration/RevShareContractsUpgraderIntegration.t.sol index 30aa82463..73109b25a 100644 --- a/test/integration/RevShareContractsUpgraderIntegration.t.sol +++ b/test/integration/RevShareContractsUpgraderIntegration.t.sol @@ -116,7 +116,9 @@ contract RevShareContractsUpgraderIntegrationTest is IntegrationBase { chain.portal, chain.l1Messenger, chain.withdrawalGasLimit, - OP_MAINNET_L1_MESSENGER + OP_MAINNET_L1_MESSENGER, + OP_MAINNET_PORTAL, + OP_MAINNET_FEES_RECIPIENT ); } } diff --git a/test/integration/RevSharePostTaskAssertions.t.sol b/test/integration/RevSharePostTaskAssertions.t.sol index 988828033..5823a9d82 100644 --- a/test/integration/RevSharePostTaskAssertions.t.sol +++ b/test/integration/RevSharePostTaskAssertions.t.sol @@ -44,6 +44,8 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { address internal _portal; address internal _l1Messenger; address internal _opL1Messenger; + address internal _opPortal; + address internal _opFeesRecipient; // Expected values from env vars uint256 internal _expectedMinWithdrawalAmount; @@ -74,6 +76,8 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { _portal = vm.envOr("OPTIMISM_PORTAL", address(0)); _l1Messenger = vm.envOr("L1_MESSENGER", address(0)); _opL1Messenger = vm.envOr("OP_L1_MESSENGER", OP_MAINNET_L1_MESSENGER); // Defaults to mainnet + _opPortal = vm.envOr("OP_PORTAL", OP_MAINNET_PORTAL); // Defaults to mainnet + _opFeesRecipient = vm.envOr("OP_FEES_RECIPIENT", OP_MAINNET_FEES_RECIPIENT); // Defaults to mainnet // Expected values to verify against on-chain state _expectedMinWithdrawalAmount = vm.envOr("MIN_WITHDRAWAL_AMOUNT", uint256(0)); @@ -180,7 +184,9 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { _portal, _l1Messenger, _expectedWithdrawalGasLimit, - _opL1Messenger + _opL1Messenger, + _opPortal, + _opFeesRecipient ); } } diff --git a/test/integration/RevShareSetupIntegration.t.sol b/test/integration/RevShareSetupIntegration.t.sol index 0d6e20e84..086b1cc1b 100644 --- a/test/integration/RevShareSetupIntegration.t.sol +++ b/test/integration/RevShareSetupIntegration.t.sol @@ -229,7 +229,9 @@ contract RevShareSetupIntegrationTest is IntegrationBase { chain.portal, chain.l1Messenger, chain.withdrawalGasLimit, - OP_MAINNET_L1_MESSENGER + OP_MAINNET_L1_MESSENGER, + OP_MAINNET_PORTAL, + OP_MAINNET_FEES_RECIPIENT ); } } From c759b225532982fccdc349e5c95d02624a5f9f43 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:30:23 -0300 Subject: [PATCH 5/6] chore: create strcuts to organize inputs and remove some mainnet defaults --- test/integration/IntegrationBase.t.sol | 131 ++++++++++-------- ...RevShareContractsUpgraderIntegration.t.sol | 28 ++-- .../RevSharePostTaskAssertions.t.sol | 59 +++++--- .../RevShareSetupIntegration.t.sol | 28 ++-- 4 files changed, 138 insertions(+), 108 deletions(-) diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index b75fa5b8e..f7b87dd31 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -39,11 +39,11 @@ abstract contract IntegrationBase is Test { address internal constant INK_MAINNET_L1_MESSENGER = 0x69d3Cf86B2Bf1a9e99875B7e2D9B6a84426c171f; address internal constant SONEIUM_MAINNET_L1_MESSENGER = 0x9CF951E3F74B644e621b36Ca9cea147a78D4c39f; - // FeesDepositor configuration (triggers deposit to OP Mainnet when balance >= threshold) + // FeesDepositor configuration (triggers deposit to OP when balance >= threshold) uint256 internal constant FEES_DEPOSITOR_THRESHOLD = 2 ether; - // OP Mainnet fees recipient (OPM multisig) - target for FeesDepositor deposits - address internal constant OP_MAINNET_FEES_RECIPIENT = 0x16A27462B4D61BDD72CbBabd3E43e11791F7A28c; + // FeesDepositor target on OP Mainnet (OPM multisig) + address internal constant OP_MAINNET_FEES_DEPOSITOR_TARGET = 0x16A27462B4D61BDD72CbBabd3E43e11791F7A28c; // Simulation flag for task execution bool internal constant IS_SIMULATE = true; @@ -77,6 +77,38 @@ abstract contract IntegrationBase is Test { string name; } + /// @notice Configuration for the chain being tested (L2 -> L1 withdrawal) + /// @param l1ForkId Fork ID for the L1 chain + /// @param l2ForkId Fork ID for the L2 chain being tested + /// @param l1Withdrawer L1Withdrawer contract address on L2 + /// @param l1WithdrawalRecipient Target address on L1 that receives withdrawals (e.g., FeesDepositor) + /// @param expectedWithdrawalAmount Expected ETH amount to be withdrawn + /// @param portal OptimismPortal address on L1 for this chain + /// @param l1Messenger L1CrossDomainMessenger address for this chain + /// @param withdrawalGasLimit Gas limit for the L2->L1 withdrawal message + struct ChainConfig { + uint256 l1ForkId; + uint256 l2ForkId; + address l1Withdrawer; + address l1WithdrawalRecipient; + uint256 expectedWithdrawalAmount; + address portal; + address l1Messenger; + uint32 withdrawalGasLimit; + } + + /// @notice Configuration for OP chain where FeesDepositor forwards funds (L1 -> OP L2) + /// @param opL2ForkId Fork ID for the OP L2 chain + /// @param opL1Messenger L1CrossDomainMessenger address for OP chain + /// @param opPortal OptimismPortal address on L1 for OP chain + /// @param feesDepositorTarget Target address on OP L2 that receives funds from FeesDepositor + struct OPConfig { + uint256 opL2ForkId; + address opL1Messenger; + address opPortal; + address feesDepositorTarget; + } + // Array to store all L2 chain configurations L2ChainConfig[] internal l2Chains; @@ -323,104 +355,81 @@ abstract contract IntegrationBase is Test { } /// @notice Execute disburseFees and assert that it triggers a withdrawal with the expected amount - /// @param _l1ForkId The L1 fork ID - /// @param _forkId The fork ID of the L2 chain to test - /// @param _opL2ForkId The OP L2 fork ID (for relaying L1→L2 deposits from FeesDepositor) - /// @param _l1Withdrawer The L1Withdrawer address that emits the WithdrawalInitiated event - /// @param _l1WithdrawalRecipient The expected recipient of the withdrawal - /// @param _expectedWithdrawalAmount The expected withdrawal amount - /// @param _portal The OptimismPortal address for this L2 chain - /// @param _l1Messenger The L1CrossDomainMessenger address for this L2 chain - /// @param _withdrawalGasLimit The gas limit used for L1 withdrawals - /// @param _opL1Messenger The OP L1CrossDomainMessenger address (mainnet or sepolia) - /// @param _opPortal The OP Portal address (mainnet or sepolia) - where FeesDepositor deposits - /// @param _opFeesRecipient The OP fees recipient address (mainnet or sepolia) - function _executeDisburseAndAssertWithdrawal( - uint256 _l1ForkId, - uint256 _forkId, - uint256 _opL2ForkId, - address _l1Withdrawer, - address _l1WithdrawalRecipient, - uint256 _expectedWithdrawalAmount, - address _portal, - address _l1Messenger, - uint32 _withdrawalGasLimit, - address _opL1Messenger, - address _opPortal, - address _opFeesRecipient - ) internal { - vm.selectFork(_forkId); + /// @param _chainConfig Configuration for the chain being tested (L2 -> L1 withdrawal) + /// @param _opConfig Configuration for OP chain where FeesDepositor forwards funds (L1 -> OP L2) + function _executeDisburseAndAssertWithdrawal(ChainConfig memory _chainConfig, OPConfig memory _opConfig) internal { + vm.selectFork(_chainConfig.l2ForkId); vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); - vm.expectEmit(true, true, true, true, _l1Withdrawer); - emit WithdrawalInitiated(_l1WithdrawalRecipient, _expectedWithdrawalAmount); + vm.expectEmit(true, true, true, true, _chainConfig.l1Withdrawer); + emit WithdrawalInitiated(_chainConfig.l1WithdrawalRecipient, _chainConfig.expectedWithdrawalAmount); IFeeSplitter(FEE_SPLITTER).disburseFees(); // Relay the withdrawal message to L1 - vm.selectFork(_l1ForkId); + vm.selectFork(_chainConfig.l1ForkId); - if (_expectedWithdrawalAmount >= FEES_DEPOSITOR_THRESHOLD) { + if (_chainConfig.expectedWithdrawalAmount >= FEES_DEPOSITOR_THRESHOLD) { // Expect TransactionDeposited event from OP Portal // Note: FeesDepositor calls L1CrossDomainMessenger.sendMessage(), which calls OptimismPortal.depositTransaction() // The 'from' address in TransactionDeposited is the aliased L1CrossDomainMessenger (not the FeesDepositor) - vm.expectEmit(true, true, true, false, _opPortal); + vm.expectEmit(true, true, true, false, _opConfig.opPortal); emit TransactionDeposited( - AddressAliasHelper.applyL1ToL2Alias(_opL1Messenger), // aliased L1CrossDomainMessenger + AddressAliasHelper.applyL1ToL2Alias(_opConfig.opL1Messenger), // aliased L1CrossDomainMessenger L2_CROSS_DOMAIN_MESSENGER, // L2 CrossDomainMessenger 0, "" ); _relayL2ToL1Message( - _portal, - _l1Messenger, - _l1Withdrawer, // sender on L2 - _l1WithdrawalRecipient, // target on L1 - _expectedWithdrawalAmount, // value - _withdrawalGasLimit, // minGasLimit + _chainConfig.portal, + _chainConfig.l1Messenger, + _chainConfig.l1Withdrawer, // sender on L2 + _chainConfig.l1WithdrawalRecipient, // target on L1 + _chainConfig.expectedWithdrawalAmount, // value + _chainConfig.withdrawalGasLimit, // minGasLimit "" // data (empty for ETH transfer) ); // Now relay the deposit from L1 to OP L2 - vm.selectFork(_opL2ForkId); + vm.selectFork(_opConfig.opL2ForkId); - uint256 recipientBalanceBefore = _opFeesRecipient.balance; + uint256 recipientBalanceBefore = _opConfig.feesDepositorTarget.balance; - // Relay the L1→L2 message (simple ETH transfer to fees recipient) - address aliasedOpL1Messenger = AddressAliasHelper.applyL1ToL2Alias(_opL1Messenger); + // Relay the L1→L2 message (simple ETH transfer to FeesDepositor target) + address aliasedOpL1Messenger = AddressAliasHelper.applyL1ToL2Alias(_opConfig.opL1Messenger); _relayL1ToL2Message( aliasedOpL1Messenger, - _l1WithdrawalRecipient, // sender (FeesDepositor) - _opFeesRecipient, // target (fees recipient) - _expectedWithdrawalAmount, + _chainConfig.l1WithdrawalRecipient, // sender (FeesDepositor) + _opConfig.feesDepositorTarget, // target (OP fees recipient) + _chainConfig.expectedWithdrawalAmount, 200_000, // gas limit for simple ETH transfer "" // empty data for ETH transfer ); - uint256 recipientBalanceAfter = _opFeesRecipient.balance; + uint256 recipientBalanceAfter = _opConfig.feesDepositorTarget.balance; assertEq( recipientBalanceAfter - recipientBalanceBefore, - _expectedWithdrawalAmount, - "OP fees recipient should receive the withdrawal amount" + _chainConfig.expectedWithdrawalAmount, + "FeesDepositor target should receive the withdrawal amount" ); } else { // FeesDepositor holds the ETH (below threshold) - uint256 recipientBalanceBefore = _l1WithdrawalRecipient.balance; + uint256 recipientBalanceBefore = _chainConfig.l1WithdrawalRecipient.balance; _relayL2ToL1Message( - _portal, - _l1Messenger, - _l1Withdrawer, // sender on L2 - _l1WithdrawalRecipient, // target on L1 - _expectedWithdrawalAmount, // value - _withdrawalGasLimit, // minGasLimit + _chainConfig.portal, + _chainConfig.l1Messenger, + _chainConfig.l1Withdrawer, // sender on L2 + _chainConfig.l1WithdrawalRecipient, // target on L1 + _chainConfig.expectedWithdrawalAmount, // value + _chainConfig.withdrawalGasLimit, // minGasLimit "" // data (empty for ETH transfer) ); - uint256 recipientBalanceAfter = _l1WithdrawalRecipient.balance; + uint256 recipientBalanceAfter = _chainConfig.l1WithdrawalRecipient.balance; assertEq( recipientBalanceAfter - recipientBalanceBefore, - _expectedWithdrawalAmount, + _chainConfig.expectedWithdrawalAmount, "L1 recipient should receive the withdrawal amount" ); } diff --git a/test/integration/RevShareContractsUpgraderIntegration.t.sol b/test/integration/RevShareContractsUpgraderIntegration.t.sol index 73109b25a..244bcb14c 100644 --- a/test/integration/RevShareContractsUpgraderIntegration.t.sol +++ b/test/integration/RevShareContractsUpgraderIntegration.t.sol @@ -107,18 +107,22 @@ contract RevShareContractsUpgraderIntegrationTest is IntegrationBase { chain.minWithdrawalAmount, chain.l1WithdrawalRecipient, chain.withdrawalGasLimit ); _executeDisburseAndAssertWithdrawal( - _mainnetForkId, - chain.forkId, - _opMainnetForkId, - l1Withdrawer, - chain.l1WithdrawalRecipient, - expectedWithdrawalAmount, - chain.portal, - chain.l1Messenger, - chain.withdrawalGasLimit, - OP_MAINNET_L1_MESSENGER, - OP_MAINNET_PORTAL, - OP_MAINNET_FEES_RECIPIENT + ChainConfig({ + l1ForkId: _mainnetForkId, + l2ForkId: chain.forkId, + l1Withdrawer: l1Withdrawer, + l1WithdrawalRecipient: chain.l1WithdrawalRecipient, + expectedWithdrawalAmount: expectedWithdrawalAmount, + portal: chain.portal, + l1Messenger: chain.l1Messenger, + withdrawalGasLimit: chain.withdrawalGasLimit + }), + OPConfig({ + opL2ForkId: _opMainnetForkId, + opL1Messenger: OP_MAINNET_L1_MESSENGER, + opPortal: OP_MAINNET_PORTAL, + feesDepositorTarget: OP_MAINNET_FEES_DEPOSITOR_TARGET + }) ); } } diff --git a/test/integration/RevSharePostTaskAssertions.t.sol b/test/integration/RevSharePostTaskAssertions.t.sol index 5823a9d82..c69907543 100644 --- a/test/integration/RevSharePostTaskAssertions.t.sol +++ b/test/integration/RevSharePostTaskAssertions.t.sol @@ -14,10 +14,12 @@ import {ISuperchainRevSharesCalculator} from "src/interfaces/ISuperchainRevShare /// @dev Required environment variables: /// - RPC_URL: L2 RPC URL to create fork /// - L1_RPC_URL: L1 RPC URL to create fork (for withdrawal relay tests) -/// - OP_RPC_URL: OP L2 RPC URL for L1→L2 relay tests (defaults to RPC_URL) +/// - OP_RPC_URL: OP L2 RPC URL for L1→L2 relay tests /// - OPTIMISM_PORTAL: Portal address for the chain /// - L1_MESSENGER: L1CrossDomainMessenger address for the chain -/// - OP_L1_MESSENGER: OP L1CrossDomainMessenger address (defaults to mainnet) +/// - OP_L1_MESSENGER: OP L1CrossDomainMessenger address +/// - OP_PORTAL: OP Portal address where FeesDepositor deposits to +/// - FEES_DEPOSITOR_TARGET: Target address that FeesDepositor sends funds to on OP L2 /// - MIN_WITHDRAWAL_AMOUNT: Expected min withdrawal amount for L1Withdrawer (wei) /// - L1_WITHDRAWAL_RECIPIENT: Expected L1 withdrawal recipient address /// - WITHDRAWAL_GAS_LIMIT: Expected gas limit for withdrawals @@ -27,14 +29,16 @@ import {ISuperchainRevSharesCalculator} from "src/interfaces/ISuperchainRevShare /// RPC_URL="https://revshare-alpha-0.optimism.io" \ /// L1_RPC_URL="https://ethereum-sepolia-rpc.publicnode.com" \ /// OP_RPC_URL="https://sepolia.optimism.io" \ -/// OPTIMISM_PORTAL="0x5b03d83e3355cdb33fa89bafc598128c2992e0ac" \ -/// L1_MESSENGER="0x5bb384968c190f6452b8db4f6ba8a282005947b3" \ +/// OPTIMISM_PORTAL="0x176e57217e8824e26cd0f78cd6de2a0655feb675" \ +/// L1_MESSENGER="0xb24a72a720e0ddec249379dc04bcb1a9c780c7c6" \ /// OP_L1_MESSENGER="0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef" \ +/// OP_PORTAL="0x16Fc5058F25648194471939df75CF27A2fdC48BC" \ +/// FEES_DEPOSITOR_TARGET="0x7ca800c55ad9C745AC84FdeEfaf4522F4Df07577" \ /// MIN_WITHDRAWAL_AMOUNT="10000000000000000000" \ /// L1_WITHDRAWAL_RECIPIENT="0x81c01427DFA9A2512b4EBf1462868856BA4aA91a" \ /// WITHDRAWAL_GAS_LIMIT="1000000" \ /// CHAIN_FEES_RECIPIENT="0x455A1115C97cb0E2b24B064C00a9E13872cC37ca" \ -/// forge test --match-contract RevSharePostTaskAssertionsTest +/// forge test --match-contract RevSharePostTaskAssertionsTest -vvv /// ``` contract RevSharePostTaskAssertionsTest is IntegrationBase { // Fork ID @@ -45,7 +49,7 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { address internal _l1Messenger; address internal _opL1Messenger; address internal _opPortal; - address internal _opFeesRecipient; + address internal _feesDepositorTarget; // Expected values from env vars uint256 internal _expectedMinWithdrawalAmount; @@ -72,12 +76,12 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { // Read env vars with defaults to detect if they're set string memory rpcUrl = vm.envOr("RPC_URL", string("")); string memory l1RpcUrl = vm.envOr("L1_RPC_URL", string("")); - string memory opRpcUrl = vm.envOr("OP_RPC_URL", rpcUrl); // Defaults to RPC_URL + string memory opRpcUrl = vm.envOr("OP_RPC_URL", string("")); _portal = vm.envOr("OPTIMISM_PORTAL", address(0)); _l1Messenger = vm.envOr("L1_MESSENGER", address(0)); - _opL1Messenger = vm.envOr("OP_L1_MESSENGER", OP_MAINNET_L1_MESSENGER); // Defaults to mainnet - _opPortal = vm.envOr("OP_PORTAL", OP_MAINNET_PORTAL); // Defaults to mainnet - _opFeesRecipient = vm.envOr("OP_FEES_RECIPIENT", OP_MAINNET_FEES_RECIPIENT); // Defaults to mainnet + _opL1Messenger = vm.envOr("OP_L1_MESSENGER", address(0)); + _opPortal = vm.envOr("OP_PORTAL", address(0)); + _feesDepositorTarget = vm.envOr("FEES_DEPOSITOR_TARGET", address(0)); // Expected values to verify against on-chain state _expectedMinWithdrawalAmount = vm.envOr("MIN_WITHDRAWAL_AMOUNT", uint256(0)); @@ -88,13 +92,18 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { // Check if all required env vars are set bool hasRpcUrl = bytes(rpcUrl).length > 0; bool hasL1RpcUrl = bytes(l1RpcUrl).length > 0; + bool hasOpRpcUrl = bytes(opRpcUrl).length > 0; bool hasPortal = _portal != address(0); bool hasL1Messenger = _l1Messenger != address(0); + bool hasOpL1Messenger = _opL1Messenger != address(0); + bool hasOpPortal = _opPortal != address(0); + bool hasFeesDepositorTarget = _feesDepositorTarget != address(0); bool hasExpectedL1WithdrawalRecipient = _expectedL1WithdrawalRecipient != address(0); bool hasExpectedWithdrawalGasLimit = _expectedWithdrawalGasLimit != 0; bool hasExpectedChainFeesRecipient = _expectedChainFeesRecipient != address(0); - _isEnabled = hasRpcUrl && hasL1RpcUrl && hasPortal && hasL1Messenger && hasExpectedL1WithdrawalRecipient + _isEnabled = hasRpcUrl && hasL1RpcUrl && hasOpRpcUrl && hasPortal && hasL1Messenger && hasOpL1Messenger + && hasOpPortal && hasFeesDepositorTarget && hasExpectedL1WithdrawalRecipient && hasExpectedWithdrawalGasLimit && hasExpectedChainFeesRecipient; if (_isEnabled) { @@ -175,18 +184,22 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { uint256 expectedWithdrawalAmount = l1WithdrawerBalanceAfter + secondShare; _executeDisburseAndAssertWithdrawal( - _mainnetForkId, - _l2ForkId, - _opMainnetForkId, - _l1Withdrawer, - _expectedL1WithdrawalRecipient, - expectedWithdrawalAmount, - _portal, - _l1Messenger, - _expectedWithdrawalGasLimit, - _opL1Messenger, - _opPortal, - _opFeesRecipient + ChainConfig({ + l1ForkId: _mainnetForkId, + l2ForkId: _l2ForkId, + l1Withdrawer: _l1Withdrawer, + l1WithdrawalRecipient: _expectedL1WithdrawalRecipient, + expectedWithdrawalAmount: expectedWithdrawalAmount, + portal: _portal, + l1Messenger: _l1Messenger, + withdrawalGasLimit: _expectedWithdrawalGasLimit + }), + OPConfig({ + opL2ForkId: _opMainnetForkId, + opL1Messenger: _opL1Messenger, + opPortal: _opPortal, + feesDepositorTarget: _feesDepositorTarget + }) ); } } diff --git a/test/integration/RevShareSetupIntegration.t.sol b/test/integration/RevShareSetupIntegration.t.sol index 086b1cc1b..084a7f230 100644 --- a/test/integration/RevShareSetupIntegration.t.sol +++ b/test/integration/RevShareSetupIntegration.t.sol @@ -220,18 +220,22 @@ contract RevShareSetupIntegrationTest is IntegrationBase { chain.minWithdrawalAmount, chain.l1WithdrawalRecipient, chain.withdrawalGasLimit ); _executeDisburseAndAssertWithdrawal( - _mainnetForkId, - chain.forkId, - _opMainnetForkId, - l1Withdrawer, - chain.l1WithdrawalRecipient, - expectedWithdrawalAmount, - chain.portal, - chain.l1Messenger, - chain.withdrawalGasLimit, - OP_MAINNET_L1_MESSENGER, - OP_MAINNET_PORTAL, - OP_MAINNET_FEES_RECIPIENT + ChainConfig({ + l1ForkId: _mainnetForkId, + l2ForkId: chain.forkId, + l1Withdrawer: l1Withdrawer, + l1WithdrawalRecipient: chain.l1WithdrawalRecipient, + expectedWithdrawalAmount: expectedWithdrawalAmount, + portal: chain.portal, + l1Messenger: chain.l1Messenger, + withdrawalGasLimit: chain.withdrawalGasLimit + }), + OPConfig({ + opL2ForkId: _opMainnetForkId, + opL1Messenger: OP_MAINNET_L1_MESSENGER, + opPortal: OP_MAINNET_PORTAL, + feesDepositorTarget: OP_MAINNET_FEES_DEPOSITOR_TARGET + }) ); } } From 3d10ea5554cc21b1b51409d058fe08b1365aa0b5 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:39:36 -0300 Subject: [PATCH 6/6] chore: polish --- test/integration/IntegrationBase.t.sol | 23 +++++++++++++---- .../RevSharePostTaskAssertions.t.sol | 25 ++++++------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index f7b87dd31..08e2d7bb2 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -62,10 +62,21 @@ abstract contract IntegrationBase is Test { // Extra gas buffer added to the minimum gas limit for the relayMessage function uint64 internal constant RELAY_GAS_OVERHEAD = 700_000; + // Gas limit for simple ETH transfers via L1→L2 relay (FeesDepositor → OP L2) + uint256 internal constant L1_TO_L2_ETH_TRANSFER_GAS_LIMIT = 200_000; + // Counter for unique L1→L2 message nonces (to avoid collisions on forks) uint240 internal _l1ToL2NonceCounter; - // L2 chain configuration struct + /// @notice L2 chain configuration for multi-chain integration tests + /// @param forkId Fork ID for this L2 chain + /// @param portal OptimismPortal address on L1 for this chain + /// @param l1Messenger L1CrossDomainMessenger address for this chain + /// @param minWithdrawalAmount Minimum withdrawal amount for L1Withdrawer (wei) + /// @param l1WithdrawalRecipient Target address on L1 that receives withdrawals + /// @param withdrawalGasLimit Gas limit for L2->L1 withdrawal messages + /// @param chainFeesRecipient Chain fees recipient address (85% share) + /// @param name Human-readable chain name for logging struct L2ChainConfig { uint256 forkId; address portal; @@ -402,7 +413,7 @@ abstract contract IntegrationBase is Test { _chainConfig.l1WithdrawalRecipient, // sender (FeesDepositor) _opConfig.feesDepositorTarget, // target (OP fees recipient) _chainConfig.expectedWithdrawalAmount, - 200_000, // gas limit for simple ETH transfer + L1_TO_L2_ETH_TRANSFER_GAS_LIMIT, "" // empty data for ETH transfer ); @@ -437,9 +448,11 @@ abstract contract IntegrationBase is Test { /// @notice Relay a message from L2 to L1 via the CrossDomainMessenger /// @dev This simulates the L2->L1 message relay by: - /// 1. Setting the portal's l2Sender to the L2CrossDomainMessenger - /// 2. Calling relayMessage on the L1CrossDomainMessenger from the portal - /// 3. Resetting the l2Sender back to the default value + /// 1. Getting the message nonce from the L1 messenger + /// 2. Setting the portal's l2Sender to the L2CrossDomainMessenger + /// 3. Dealing ETH to the portal so it can send value with the message + /// 4. Calling relayMessage on the L1CrossDomainMessenger from the portal + /// 5. Resetting the l2Sender back to the default value /// @param _portal The OptimismPortal address /// @param _l1Messenger The L1CrossDomainMessenger address /// @param _sender The sender address on L2 diff --git a/test/integration/RevSharePostTaskAssertions.t.sol b/test/integration/RevSharePostTaskAssertions.t.sol index c69907543..9fcac89b2 100644 --- a/test/integration/RevSharePostTaskAssertions.t.sol +++ b/test/integration/RevSharePostTaskAssertions.t.sol @@ -89,22 +89,12 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { _expectedWithdrawalGasLimit = uint32(vm.envOr("WITHDRAWAL_GAS_LIMIT", uint256(0))); _expectedChainFeesRecipient = vm.envOr("CHAIN_FEES_RECIPIENT", address(0)); - // Check if all required env vars are set - bool hasRpcUrl = bytes(rpcUrl).length > 0; - bool hasL1RpcUrl = bytes(l1RpcUrl).length > 0; - bool hasOpRpcUrl = bytes(opRpcUrl).length > 0; - bool hasPortal = _portal != address(0); - bool hasL1Messenger = _l1Messenger != address(0); - bool hasOpL1Messenger = _opL1Messenger != address(0); - bool hasOpPortal = _opPortal != address(0); - bool hasFeesDepositorTarget = _feesDepositorTarget != address(0); - bool hasExpectedL1WithdrawalRecipient = _expectedL1WithdrawalRecipient != address(0); - bool hasExpectedWithdrawalGasLimit = _expectedWithdrawalGasLimit != 0; - bool hasExpectedChainFeesRecipient = _expectedChainFeesRecipient != address(0); - - _isEnabled = hasRpcUrl && hasL1RpcUrl && hasOpRpcUrl && hasPortal && hasL1Messenger && hasOpL1Messenger - && hasOpPortal && hasFeesDepositorTarget && hasExpectedL1WithdrawalRecipient - && hasExpectedWithdrawalGasLimit && hasExpectedChainFeesRecipient; + // Check if all required env vars are set (combined to avoid stack too deep) + _isEnabled = bytes(rpcUrl).length > 0 && bytes(l1RpcUrl).length > 0 && bytes(opRpcUrl).length > 0 + && _portal != address(0) && _l1Messenger != address(0) && _opL1Messenger != address(0) + && _opPortal != address(0) && _feesDepositorTarget != address(0) && _expectedMinWithdrawalAmount != 0 + && _expectedL1WithdrawalRecipient != address(0) && _expectedWithdrawalGasLimit != 0 + && _expectedChainFeesRecipient != address(0); if (_isEnabled) { _mainnetForkId = vm.createFork(l1RpcUrl); @@ -180,7 +170,8 @@ contract RevSharePostTaskAssertionsTest is IntegrationBase { vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); // Calculate expected withdrawal amount (current balance + new share) - uint256 secondShare = (secondVaultFunding * 45) / 100; + // share = netRevenue * 15% = vaultFunding * 3 * 15 / 100 + uint256 secondShare = (secondVaultFunding * 3 * 15) / 100; uint256 expectedWithdrawalAmount = l1WithdrawerBalanceAfter + secondShare; _executeDisburseAndAssertWithdrawal(