diff --git a/src/template/RevShareSetup.sol b/src/template/RevShareSetup.sol new file mode 100644 index 000000000..b44b71f2d --- /dev/null +++ b/src/template/RevShareSetup.sol @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {VmSafe} from "forge-std/Vm.sol"; +import {stdToml} from "forge-std/StdToml.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +import {OPCMTaskBase} from "src/tasks/types/OPCMTaskBase.sol"; +import {Action} from "src/libraries/MultisigTypes.sol"; +import {MultisigTaskPrinter} from "src/libraries/MultisigTaskPrinter.sol"; +import {RevShareContractsUpgrader} from "src/RevShareContractsUpgrader.sol"; +import {FeeSplitterSetup} from "src/libraries/FeeSplitterSetup.sol"; + +/// @notice Task for setting up revenue sharing on OP Stack chains after predeploys upgrade. +contract RevShareSetup is OPCMTaskBase { + using stdToml for string; + using EnumerableSet for EnumerableSet.AddressSet; + + /// @notice RevShareContractsUpgrader address + address public REV_SHARE_UPGRADER; + + /// @notice RevShare configurations + RevShareContractsUpgrader.RevShareConfig[] internal revShareConfigs; + + /// @notice Names in the SimpleAddressRegistry that are expected to be written during this task. + function _taskStorageWrites() internal pure virtual override returns (string[] memory) { + return new string[](0); + } + + /// @notice Returns an array of strings that refer to contract names in the address registry. + function _taskBalanceChanges() internal view virtual override returns (string[] memory) { + return new string[](0); + } + + /// @notice Sets the allowed storage accesses - override to add portal addresses + function _setAllowedStorageAccesses() internal virtual override { + super._setAllowedStorageAccesses(); + // Add portal addresses as they will have storage writes from depositTransaction calls + for (uint256 i; i < revShareConfigs.length; i++) { + _allowedStorageAccesses.add(revShareConfigs[i].portal); + } + } + + /// @notice Sets up the template with configurations from a TOML file. + function _templateSetup(string memory taskConfigFilePath, address) internal override { + string memory tomlContent = vm.readFile(taskConfigFilePath); + + // Load RevShareContractsUpgrader address from TOML + REV_SHARE_UPGRADER = tomlContent.readAddress(".revShareUpgrader"); + require(REV_SHARE_UPGRADER != address(0), "RevShareContractsUpgrader address cannot be zero"); + require(REV_SHARE_UPGRADER.code.length > 0, "RevShareContractsUpgrader has no code"); + vm.label(REV_SHARE_UPGRADER, "RevShareContractsUpgrader"); + + // Set RevShareContractsUpgrader as the allowed target for delegatecall + OPCM_TARGETS.push(REV_SHARE_UPGRADER); + + // Load flattened arrays from TOML + address[] memory portals = abi.decode(tomlContent.parseRaw(".portals"), (address[])); + address[] memory chainFeesRecipients = abi.decode(tomlContent.parseRaw(".chainFeesRecipients"), (address[])); + uint256[] memory minWithdrawalAmounts = + abi.decode(tomlContent.parseRaw(".l1WithdrawerMinWithdrawalAmounts"), (uint256[])); + address[] memory l1WithdrawerRecipients = + abi.decode(tomlContent.parseRaw(".l1WithdrawerRecipients"), (address[])); + uint256[] memory gasLimits = abi.decode(tomlContent.parseRaw(".l1WithdrawerGasLimits"), (uint256[])); + + // Validate all arrays have the same length + require(portals.length > 0, "No configs found"); + require( + portals.length == chainFeesRecipients.length && portals.length == minWithdrawalAmounts.length + && portals.length == l1WithdrawerRecipients.length && portals.length == gasLimits.length, + "Config arrays length mismatch" + ); + + // Validate individual configuration values and check for duplicates + for (uint256 i; i < portals.length; i++) { + // Validate portal address + require(portals[i] != address(0), string.concat("Portal address cannot be zero at index ", vm.toString(i))); + require(portals[i].code.length > 0, string.concat("Portal has no code at index ", vm.toString(i))); + + // Check for duplicate portals + for (uint256 j; j < i; j++) { + require(portals[i] != portals[j], string.concat("Duplicate portal address at index ", vm.toString(i))); + } + + // Validate chain fees recipient + require( + chainFeesRecipients[i] != address(0), + string.concat("Chain fees recipient cannot be zero at index ", vm.toString(i)) + ); + + // Validate L1 withdrawer recipient + require( + l1WithdrawerRecipients[i] != address(0), + string.concat("L1 withdrawer recipient cannot be zero at index ", vm.toString(i)) + ); + + // Validate gas limit bounds + require(gasLimits[i] > 0, string.concat("Gas limit must be greater than 0 at index ", vm.toString(i))); + require( + gasLimits[i] <= type(uint32).max, + string.concat("Gas limit exceeds uint32 max at index ", vm.toString(i)) + ); + } + + // Construct RevShare configs array from flattened arrays + for (uint256 i; i < portals.length; i++) { + revShareConfigs.push( + RevShareContractsUpgrader.RevShareConfig({ + portal: portals[i], + l1WithdrawerConfig: FeeSplitterSetup.L1WithdrawerConfig({ + minWithdrawalAmount: minWithdrawalAmounts[i], + recipient: l1WithdrawerRecipients[i], + gasLimit: uint32(gasLimits[i]) + }), + chainFeesRecipient: chainFeesRecipients[i] + }) + ); + } + } + + /// @notice Builds the actions for executing the operations. + function _build(address) internal override { + // Delegatecall into RevShareContractsUpgrader + // OPCMTaskBase uses Multicall3Delegatecall, so this delegatecall will be captured as an action + (bool success,) = + REV_SHARE_UPGRADER.delegatecall(abi.encodeCall(RevShareContractsUpgrader.setupRevShare, (revShareConfigs))); + require(success, "RevShareSetup: Delegatecall failed"); + } + + /// @notice This method performs all validations and assertions that verify the calls executed as expected. + function _validate(VmSafe.AccountAccess[] memory, Action[] memory _actions, address) internal view override { + MultisigTaskPrinter.printTitle("Validating delegatecall to RevShareContractsUpgrader"); + + // For OPCM tasks using delegatecall, we validate that the delegatecall was made correctly. + // The actual portal calls happen inside the delegatecall and are validated by integration tests. + + require(_actions.length == 1, "Expected exactly one action"); + + Action memory action = _actions[0]; + require(action.target == REV_SHARE_UPGRADER, "Delegatecall to RevShareContractsUpgrader not found"); + require(action.value == 0, "Call value must be 0 for delegatecall"); + + // Verify it's calling setupRevShare + bytes4 selector = bytes4(action.arguments); + require( + selector == RevShareContractsUpgrader.setupRevShare.selector, "Wrong function selector for delegatecall" + ); + + // Decode and validate the revShareConfigs argument + // Skip the first 4 bytes (function selector) and decode the rest + RevShareContractsUpgrader.RevShareConfig[] memory configs; + { + bytes memory args = action.arguments; + bytes memory argsWithoutSelector = new bytes(args.length - 4); + for (uint256 j = 4; j < args.length; j++) { + argsWithoutSelector[j - 4] = args[j]; + } + configs = abi.decode(argsWithoutSelector, (RevShareContractsUpgrader.RevShareConfig[])); + } + + // Validate each config + require(configs.length > 0, "No configs provided"); + require(configs.length == revShareConfigs.length, "Config length mismatch"); + + for (uint256 i; i < configs.length; i++) { + RevShareContractsUpgrader.RevShareConfig memory config = configs[i]; + + // Validate portal address is not zero + require(config.portal != address(0), "Portal address cannot be zero"); + + // Validate L1 withdrawer config + require(config.l1WithdrawerConfig.recipient != address(0), "L1 withdrawer recipient cannot be zero"); + require(config.l1WithdrawerConfig.gasLimit > 0, "Gas limit must be greater than 0"); + + // Validate chain fees recipient + require(config.chainFeesRecipient != address(0), "Chain fees recipient cannot be zero"); + + // Validate config matches the expected config from template setup + require(config.portal == revShareConfigs[i].portal, "Portal address mismatch"); + require( + config.l1WithdrawerConfig.minWithdrawalAmount + == revShareConfigs[i].l1WithdrawerConfig.minWithdrawalAmount, + "Min withdrawal amount mismatch" + ); + require( + config.l1WithdrawerConfig.recipient == revShareConfigs[i].l1WithdrawerConfig.recipient, + "L1 withdrawer recipient mismatch" + ); + require( + config.l1WithdrawerConfig.gasLimit == revShareConfigs[i].l1WithdrawerConfig.gasLimit, + "Gas limit mismatch" + ); + require(config.chainFeesRecipient == revShareConfigs[i].chainFeesRecipient, "Chain fees recipient mismatch"); + } + } + + /// @notice Override to return a list of addresses that should not be checked for code length. + function _getCodeExceptions() internal view virtual override returns (address[] memory) { + return new address[](0); + } +} diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index ac83b8c3f..641e485ec 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -5,10 +5,60 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; import {console2} from "forge-std/console2.sol"; import {AddressAliasHelper} from "@eth-optimism-bedrock/src/vendor/AddressAliasHelper.sol"; +import {RevShareContractsUpgrader} from "src/RevShareContractsUpgrader.sol"; +import {Predeploys} from "@eth-optimism-bedrock/src/libraries/Predeploys.sol"; +import {IFeeSplitter} from "src/interfaces/IFeeSplitter.sol"; +import {IFeeVault} from "src/interfaces/IFeeVault.sol"; +import {IL1Withdrawer} from "src/interfaces/IL1Withdrawer.sol"; +import {ISuperchainRevSharesCalculator} from "src/interfaces/ISuperchainRevSharesCalculator.sol"; /// @title IntegrationBase /// @notice Base contract for integration tests with L1->L2 deposit transaction relay functionality abstract contract IntegrationBase is Test { + // Events for testing + event WithdrawalInitiated(address indexed recipient, uint256 amount); + + // Fork IDs + uint256 internal _mainnetForkId; + uint256 internal _opMainnetForkId; + uint256 internal _inkMainnetForkId; + + // Shared upgrader contract + RevShareContractsUpgrader public revShareUpgrader; + + // L1 addresses + address internal constant PROXY_ADMIN_OWNER = 0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A; + address internal constant OP_MAINNET_PORTAL = 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed; + address internal constant INK_MAINNET_PORTAL = 0x5d66C1782664115999C47c9fA5cd031f495D3e4F; + address internal constant REV_SHARE_UPGRADER_ADDRESS = 0x0000000000000000000000000000000000001337; + + // L2 predeploys (same across all OP Stack chains) + address internal constant SEQUENCER_FEE_VAULT = 0x4200000000000000000000000000000000000011; + address internal constant OPERATOR_FEE_VAULT = 0x420000000000000000000000000000000000001b; + address internal constant BASE_FEE_VAULT = 0x4200000000000000000000000000000000000019; + address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A; + address internal constant FEE_SPLITTER = 0x420000000000000000000000000000000000002B; + + // Expected deployed contracts (deterministic CREATE2 addresses) + address internal constant OP_L1_WITHDRAWER = 0xB3AeB34b88D73Fb4832f65BEa5Bd865017fB5daC; + address internal constant OP_REV_SHARE_CALCULATOR = 0x3E806Fd8592366E850197FEC8D80608b5526Bba2; + + address internal constant INK_L1_WITHDRAWER = 0x70e26B12a578176BccCD3b7e7f58f605871c5eF7; + address internal constant INK_REV_SHARE_CALCULATOR = 0xd7a5307B4Ce92B0269903191007b95dF42552Dfa; + + // Test configuration - OP Mainnet + uint256 internal constant OP_MIN_WITHDRAWAL_AMOUNT = 350000; + address internal constant OP_L1_WITHDRAWAL_RECIPIENT = 0x0000000000000000000000000000000000000001; + uint32 internal constant OP_WITHDRAWAL_GAS_LIMIT = 800000; + address internal constant OP_CHAIN_FEES_RECIPIENT = 0x0000000000000000000000000000000000000001; + + // Test configuration - Ink Mainnet + uint256 internal constant INK_MIN_WITHDRAWAL_AMOUNT = 500000; + address internal constant INK_L1_WITHDRAWAL_RECIPIENT = 0x0000000000000000000000000000000000000002; + uint32 internal constant INK_WITHDRAWAL_GAS_LIMIT = 1000000; + address internal constant INK_CHAIN_FEES_RECIPIENT = 0x0000000000000000000000000000000000000002; + + bool internal constant IS_SIMULATE = true; /// @notice Relay all deposit transactions from L1 to multiple L2s /// @param _forkIds Array of fork IDs for each L2 chain /// @param _isSimulate If true, only process the second half of logs to avoid duplicates. @@ -17,6 +67,7 @@ abstract contract IntegrationBase is Test { /// we only process the final simulation results. /// @param _portals Array of Portal addresses corresponding to each fork. /// Only events emitted by each portal will be relayed on its corresponding L2. + function _relayAllMessages(uint256[] memory _forkIds, bool _isSimulate, address[] memory _portals) internal { require(_forkIds.length == _portals.length, "Fork IDs and portals length mismatch"); @@ -123,4 +174,128 @@ abstract contract IntegrationBase is Test { } return _result; } + + /// @notice Fund all fee vaults with specified amount + /// @param _amount Amount to fund each vault with + /// @param _forkId Fork ID to execute on + function _fundVaults(uint256 _amount, uint256 _forkId) internal { + vm.selectFork(_forkId); + vm.deal(SEQUENCER_FEE_VAULT, _amount); + vm.deal(OPERATOR_FEE_VAULT, _amount); + vm.deal(BASE_FEE_VAULT, _amount); + vm.deal(L1_FEE_VAULT, _amount); + } + + /// @notice Assert the state of all L2 contracts after upgrade + /// @param _l1Withdrawer Expected L1Withdrawer address + /// @param _revShareCalculator Expected RevShareCalculator address + /// @param _minWithdrawalAmount Expected minimum withdrawal amount for L1Withdrawer + /// @param _l1Recipient Expected recipient address for L1Withdrawer + /// @param _gasLimit Expected gas limit for L1Withdrawer + /// @param _chainFeesRecipient Expected chain fees recipient (remainder recipient) + function _assertL2State( + address _l1Withdrawer, + address _revShareCalculator, + uint256 _minWithdrawalAmount, + address _l1Recipient, + uint32 _gasLimit, + address _chainFeesRecipient + ) internal view { + // L1Withdrawer: check configuration + assertEq( + IL1Withdrawer(_l1Withdrawer).minWithdrawalAmount(), + _minWithdrawalAmount, + "L1Withdrawer minWithdrawalAmount mismatch" + ); + assertEq(IL1Withdrawer(_l1Withdrawer).recipient(), _l1Recipient, "L1Withdrawer recipient mismatch"); + assertEq(IL1Withdrawer(_l1Withdrawer).withdrawalGasLimit(), _gasLimit, "L1Withdrawer gasLimit mismatch"); + + // Rev Share Calculator: check it's linked correctly + assertEq( + ISuperchainRevSharesCalculator(_revShareCalculator).shareRecipient(), + _l1Withdrawer, + "Calculator shareRecipient should be L1Withdrawer" + ); + assertEq( + ISuperchainRevSharesCalculator(_revShareCalculator).remainderRecipient(), + _chainFeesRecipient, + "Calculator remainderRecipient mismatch" + ); + + // Fee Splitter: check calculator is set + assertEq( + IFeeSplitter(FEE_SPLITTER).sharesCalculator(), + _revShareCalculator, + "FeeSplitter calculator should be set to RevShareCalculator" + ); + + // Vaults: recipient should be fee splitter, withdrawal network should be L2, min withdrawal amount 0 + _assertFeeVaultsState(); + } + + /// @notice Assert the configuration of all fee vaults + function _assertFeeVaultsState() internal view { + _assertVaultGetters(SEQUENCER_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); + _assertVaultGetters(OPERATOR_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); + _assertVaultGetters(BASE_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); + _assertVaultGetters(L1_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); + } + + /// @notice Assert the configuration of a single fee vault + /// @param _vault The address of the fee vault + /// @param _recipient The expected recipient of the fee vault + /// @param _withdrawalNetwork The expected withdrawal network + /// @param _minWithdrawalAmount The expected minimum withdrawal amount + /// @dev Ensures both the legacy and the new getters return the same value + function _assertVaultGetters( + address _vault, + address _recipient, + IFeeVault.WithdrawalNetwork _withdrawalNetwork, + uint256 _minWithdrawalAmount + ) internal view { + // Check new getters + assertEq(IFeeVault(_vault).recipient(), _recipient, "Vault recipient mismatch"); + assertEq( + uint256(IFeeVault(_vault).withdrawalNetwork()), + uint256(_withdrawalNetwork), + "Vault withdrawalNetwork mismatch" + ); + assertEq(IFeeVault(_vault).minWithdrawalAmount(), _minWithdrawalAmount, "Vault minWithdrawalAmount mismatch"); + + // Check legacy getters (should return same values) + assertEq(IFeeVault(_vault).RECIPIENT(), _recipient, "Vault RECIPIENT (legacy) mismatch"); + assertEq( + uint256(IFeeVault(_vault).WITHDRAWAL_NETWORK()), + uint256(_withdrawalNetwork), + "Vault WITHDRAWAL_NETWORK (legacy) mismatch" + ); + assertEq( + IFeeVault(_vault).MIN_WITHDRAWAL_AMOUNT(), + _minWithdrawalAmount, + "Vault MIN_WITHDRAWAL_AMOUNT (legacy) mismatch" + ); + } + + /// @notice Execute disburseFees and assert that it triggers a withdrawal with the expected amount + /// @param _forkId The fork ID of the chain to test + /// @param _l1WithdrawalRecipient The expected recipient of the withdrawal + /// @param _expectedWithdrawalAmount The expected withdrawal amount + function _executeDisburseAndAssertWithdrawal( + uint256 _forkId, + address _l1WithdrawalRecipient, + uint256 _expectedWithdrawalAmount + ) internal { + vm.selectFork(_forkId); + vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); + + uint256 balanceBefore = Predeploys.L2_TO_L1_MESSAGE_PASSER.balance; + + vm.expectEmit(true, true, true, true); + emit WithdrawalInitiated(_l1WithdrawalRecipient, _expectedWithdrawalAmount); + IFeeSplitter(FEE_SPLITTER).disburseFees(); + + uint256 balanceAfter = Predeploys.L2_TO_L1_MESSAGE_PASSER.balance; + + assertEq(balanceAfter - balanceBefore, _expectedWithdrawalAmount); + } } diff --git a/test/integration/RevShareContractsUpgraderIntegration.t.sol b/test/integration/RevShareContractsUpgraderIntegration.t.sol index 1c26cf9a1..8ef016548 100644 --- a/test/integration/RevShareContractsUpgraderIntegration.t.sol +++ b/test/integration/RevShareContractsUpgraderIntegration.t.sol @@ -4,64 +4,10 @@ pragma solidity 0.8.15; import {RevShareContractsUpgrader} from "src/RevShareContractsUpgrader.sol"; import {RevShareUpgradeAndSetup} from "src/template/RevShareUpgradeAndSetup.sol"; import {IntegrationBase} from "./IntegrationBase.t.sol"; -import {Predeploys} from "@eth-optimism-bedrock/src/libraries/Predeploys.sol"; -import {Test} from "forge-std/Test.sol"; - -// Interfaces -import {IOptimismPortal2} from "@eth-optimism-bedrock/interfaces/L1/IOptimismPortal2.sol"; -import {IProxyAdmin} from "@eth-optimism-bedrock/interfaces/universal/IProxyAdmin.sol"; -import {ICreate2Deployer} from "src/interfaces/ICreate2Deployer.sol"; -import {IFeeSplitter} from "src/interfaces/IFeeSplitter.sol"; -import {IFeeVault} from "src/interfaces/IFeeVault.sol"; -import {IL1Withdrawer} from "src/interfaces/IL1Withdrawer.sol"; -import {ISuperchainRevSharesCalculator} from "src/interfaces/ISuperchainRevSharesCalculator.sol"; contract RevShareContractsUpgraderIntegrationTest is IntegrationBase { - RevShareContractsUpgrader public revShareUpgrader; RevShareUpgradeAndSetup public revShareTask; - // Events for testing - event WithdrawalInitiated(address indexed recipient, uint256 amount); - - // Fork IDs - uint256 internal _mainnetForkId; - uint256 internal _opMainnetForkId; - uint256 internal _inkMainnetForkId; - - // L1 addresses - address internal constant PROXY_ADMIN_OWNER = 0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A; - address internal constant OP_MAINNET_PORTAL = 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed; - address internal constant INK_MAINNET_PORTAL = 0x5d66C1782664115999C47c9fA5cd031f495D3e4F; - address internal constant REV_SHARE_UPGRADER_ADDRESS = 0x0000000000000000000000000000000000001337; - - // L2 predeploys (same across all OP Stack chains) - address internal constant SEQUENCER_FEE_VAULT = 0x4200000000000000000000000000000000000011; - address internal constant OPERATOR_FEE_VAULT = 0x420000000000000000000000000000000000001b; - address internal constant BASE_FEE_VAULT = 0x4200000000000000000000000000000000000019; - address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A; - address internal constant FEE_SPLITTER = 0x420000000000000000000000000000000000002B; - - // Expected deployed contracts (deterministic CREATE2 addresses) - address internal constant OP_L1_WITHDRAWER = 0xB3AeB34b88D73Fb4832f65BEa5Bd865017fB5daC; - address internal constant OP_REV_SHARE_CALCULATOR = 0x3E806Fd8592366E850197FEC8D80608b5526Bba2; - - address internal constant INK_L1_WITHDRAWER = 0x70e26B12a578176BccCD3b7e7f58f605871c5eF7; - address internal constant INK_REV_SHARE_CALCULATOR = 0xd7a5307B4Ce92B0269903191007b95dF42552Dfa; - - // Test configuration - OP Mainnet - uint256 internal constant OP_MIN_WITHDRAWAL_AMOUNT = 350000; - address internal constant OP_L1_WITHDRAWAL_RECIPIENT = 0x0000000000000000000000000000000000000001; - uint32 internal constant OP_WITHDRAWAL_GAS_LIMIT = 800000; - address internal constant OP_CHAIN_FEES_RECIPIENT = 0x0000000000000000000000000000000000000001; - - // Test configuration - Ink Mainnet - uint256 internal constant INK_MIN_WITHDRAWAL_AMOUNT = 500000; - address internal constant INK_L1_WITHDRAWAL_RECIPIENT = 0x0000000000000000000000000000000000000002; - uint32 internal constant INK_WITHDRAWAL_GAS_LIMIT = 1000000; - address internal constant INK_CHAIN_FEES_RECIPIENT = 0x0000000000000000000000000000000000000002; - - bool internal constant IS_SIMULATE = true; - function setUp() public { // Create forks for L1 (mainnet) and L2 (OP Mainnet) _mainnetForkId = vm.createFork("http://127.0.0.1:8545"); @@ -82,7 +28,7 @@ contract RevShareContractsUpgraderIntegrationTest is IntegrationBase { /// @notice Test the integration of upgradeAndSetupRevShare function test_upgradeAndSetupRevShare_integration() public { - // Step 1: Record logs for L1→L2 message replay + // Step 1: Record logs for L1→L2 message relay vm.recordLogs(); // Step 2: Execute task simulation @@ -137,123 +83,4 @@ contract RevShareContractsUpgraderIntegrationTest is IntegrationBase { _executeDisburseAndAssertWithdrawal(_opMainnetForkId, OP_L1_WITHDRAWAL_RECIPIENT, expectedWithdrawalAmount); _executeDisburseAndAssertWithdrawal(_inkMainnetForkId, INK_L1_WITHDRAWAL_RECIPIENT, expectedWithdrawalAmount); } - - function _fundVaults(uint256 _amount, uint256 _forkId) internal { - vm.selectFork(_forkId); - vm.deal(SEQUENCER_FEE_VAULT, _amount); - vm.deal(OPERATOR_FEE_VAULT, _amount); - vm.deal(BASE_FEE_VAULT, _amount); - vm.deal(L1_FEE_VAULT, _amount); - } - - /// @notice Assert the state of all L2 contracts after upgrade - /// @param _minWithdrawalAmount Expected minimum withdrawal amount for L1Withdrawer - /// @param _l1Recipient Expected recipient address for L1Withdrawer - /// @param _gasLimit Expected gas limit for L1Withdrawer - /// @param _chainFeesRecipient Expected chain fees recipient (remainder recipient) - function _assertL2State( - address _l1Withdrawer, - address _revShareCalculator, - uint256 _minWithdrawalAmount, - address _l1Recipient, - uint32 _gasLimit, - address _chainFeesRecipient - ) internal view { - // L1Withdrawer: check configuration - assertEq( - IL1Withdrawer(_l1Withdrawer).minWithdrawalAmount(), - _minWithdrawalAmount, - "L1Withdrawer minWithdrawalAmount mismatch" - ); - assertEq(IL1Withdrawer(_l1Withdrawer).recipient(), _l1Recipient, "L1Withdrawer recipient mismatch"); - assertEq(IL1Withdrawer(_l1Withdrawer).withdrawalGasLimit(), _gasLimit, "L1Withdrawer gasLimit mismatch"); - - // Rev Share Calculator: check it's linked correctly - assertEq( - ISuperchainRevSharesCalculator(_revShareCalculator).shareRecipient(), - _l1Withdrawer, - "Calculator shareRecipient should be L1Withdrawer" - ); - assertEq( - ISuperchainRevSharesCalculator(_revShareCalculator).remainderRecipient(), - _chainFeesRecipient, - "Calculator remainderRecipient mismatch" - ); - - // Fee Splitter: check calculator is set - assertEq( - IFeeSplitter(FEE_SPLITTER).sharesCalculator(), - _revShareCalculator, - "FeeSplitter calculator should be set to RevShareCalculator" - ); - - // Vaults: recipient should be fee splitter, withdrawal network should be L2, min withdrawal amount 0 - _assertFeeVaultsState(); - } - - /// @notice Assert the configuration of all fee vaults - function _assertFeeVaultsState() internal view { - _assertVaultGetters(SEQUENCER_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); - _assertVaultGetters(OPERATOR_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); - _assertVaultGetters(BASE_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); - _assertVaultGetters(L1_FEE_VAULT, FEE_SPLITTER, IFeeVault.WithdrawalNetwork.L2, 0); - } - - /// @notice Assert the configuration of a single fee vault - /// @param _vault The address of the fee vault - /// @param _recipient The expected recipient of the fee vault - /// @param _withdrawalNetwork The expected withdrawal network - /// @param _minWithdrawalAmount The expected minimum withdrawal amount - /// @dev Ensures both the legacy and the new getters return the same value - function _assertVaultGetters( - address _vault, - address _recipient, - IFeeVault.WithdrawalNetwork _withdrawalNetwork, - uint256 _minWithdrawalAmount - ) internal view { - // Check new getters - assertEq(IFeeVault(_vault).recipient(), _recipient, "Vault recipient mismatch"); - assertEq( - uint256(IFeeVault(_vault).withdrawalNetwork()), - uint256(_withdrawalNetwork), - "Vault withdrawalNetwork mismatch" - ); - assertEq(IFeeVault(_vault).minWithdrawalAmount(), _minWithdrawalAmount, "Vault minWithdrawalAmount mismatch"); - - // Check legacy getters (should return same values) - assertEq(IFeeVault(_vault).RECIPIENT(), _recipient, "Vault RECIPIENT (legacy) mismatch"); - assertEq( - uint256(IFeeVault(_vault).WITHDRAWAL_NETWORK()), - uint256(_withdrawalNetwork), - "Vault WITHDRAWAL_NETWORK (legacy) mismatch" - ); - assertEq( - IFeeVault(_vault).MIN_WITHDRAWAL_AMOUNT(), - _minWithdrawalAmount, - "Vault MIN_WITHDRAWAL_AMOUNT (legacy) mismatch" - ); - } - - /// @notice Execute disburseFees and assert that it triggers a withdrawal with the expected amount - /// @param _forkId The fork ID of the chain to test - /// @param _l1WithdrawalRecipient The expected recipient of the withdrawal - /// @param _expectedWithdrawalAmount The expected withdrawal amount - function _executeDisburseAndAssertWithdrawal( - uint256 _forkId, - address _l1WithdrawalRecipient, - uint256 _expectedWithdrawalAmount - ) internal { - vm.selectFork(_forkId); - vm.warp(block.timestamp + IFeeSplitter(FEE_SPLITTER).feeDisbursementInterval() + 1); - - uint256 balanceBefore = Predeploys.L2_TO_L1_MESSAGE_PASSER.balance; - - vm.expectEmit(true, true, true, true); - emit WithdrawalInitiated(_l1WithdrawalRecipient, _expectedWithdrawalAmount); - IFeeSplitter(FEE_SPLITTER).disburseFees(); - - uint256 balanceAfter = Predeploys.L2_TO_L1_MESSAGE_PASSER.balance; - - assertEq(balanceAfter - balanceBefore, _expectedWithdrawalAmount); - } } diff --git a/test/integration/RevShareSetupIntegration.t.sol b/test/integration/RevShareSetupIntegration.t.sol new file mode 100644 index 000000000..207df093d --- /dev/null +++ b/test/integration/RevShareSetupIntegration.t.sol @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {RevShareContractsUpgrader} from "src/RevShareContractsUpgrader.sol"; +import {RevShareSetup} from "src/template/RevShareSetup.sol"; +import {IntegrationBase} from "./IntegrationBase.t.sol"; +import {FeeVaultUpgrader} from "src/libraries/FeeVaultUpgrader.sol"; +import {FeeSplitterSetup} from "src/libraries/FeeSplitterSetup.sol"; +import {RevShareCommon} from "src/libraries/RevShareCommon.sol"; +import {Proxy} from "@eth-optimism-bedrock/src/universal/Proxy.sol"; + +contract RevShareSetupIntegrationTest is IntegrationBase { + RevShareSetup public revShareTask; + + // EIP-1967 storage slots for proxy (specific to RevShareSetup test) + bytes32 internal constant PROXY_IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 internal constant PROXY_OWNER_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + // Creation codes from libraries (specific to RevShareSetup test) + bytes internal OPERATOR_FEE_VAULT_CREATION_CODE = FeeVaultUpgrader.operatorFeeVaultCreationCode; + bytes internal SEQUENCER_FEE_VAULT_CREATION_CODE = FeeVaultUpgrader.sequencerFeeVaultCreationCode; + bytes internal DEFAULT_FEE_VAULT_CREATION_CODE = FeeVaultUpgrader.defaultFeeVaultCreationCode; + bytes internal FEE_SPLITTER_CREATION_CODE = FeeSplitterSetup.feeSplitterCreationCode; + + function setUp() public { + // Create forks for L1 (mainnet) and L2 (OP Mainnet) + _mainnetForkId = vm.createFork("http://127.0.0.1:8545"); + _opMainnetForkId = vm.createFork("http://127.0.0.1:9545"); + _inkMainnetForkId = vm.createFork("http://127.0.0.1:9546"); + + // Deploy contracts on L1 + vm.selectFork(_mainnetForkId); + + // Deploy RevShareContractsUpgrader and etch at predetermined address + revShareUpgrader = new RevShareContractsUpgrader(); + vm.etch(REV_SHARE_UPGRADER_ADDRESS, address(revShareUpgrader).code); + revShareUpgrader = RevShareContractsUpgrader(REV_SHARE_UPGRADER_ADDRESS); + + // Deploy RevShareSetup task + revShareTask = new RevShareSetup(); + + // Deploy implementations once to get their addresses and bytecode + address operatorFeeVaultImpl = _deployFromCreationCode(OPERATOR_FEE_VAULT_CREATION_CODE); + address sequencerFeeVaultImpl = _deployFromCreationCode(SEQUENCER_FEE_VAULT_CREATION_CODE); + address defaultFeeVaultImpl = _deployFromCreationCode(DEFAULT_FEE_VAULT_CREATION_CODE); + address feeSplitterImpl = _deployFromCreationCode(FEE_SPLITTER_CREATION_CODE); + + // Get implementation bytecodes + bytes memory operatorFeeVaultImplCode = operatorFeeVaultImpl.code; + bytes memory sequencerFeeVaultImplCode = sequencerFeeVaultImpl.code; + bytes memory defaultFeeVaultImplCode = defaultFeeVaultImpl.code; + bytes memory feeSplitterImplCode = feeSplitterImpl.code; + + // Deploy a proxy to get its bytecode + Proxy proxyTemplate = new Proxy(address(this)); + bytes memory proxyCode = address(proxyTemplate).code; + + // Etch predeploys on OP Mainnet fork + vm.selectFork(_opMainnetForkId); + _etchImplementations( + operatorFeeVaultImpl, + sequencerFeeVaultImpl, + defaultFeeVaultImpl, + feeSplitterImpl, + operatorFeeVaultImplCode, + sequencerFeeVaultImplCode, + defaultFeeVaultImplCode, + feeSplitterImplCode + ); + _setupProxyPredeploys( + proxyCode, operatorFeeVaultImpl, sequencerFeeVaultImpl, defaultFeeVaultImpl, feeSplitterImpl + ); + + // Etch predeploys on Ink Mainnet fork + vm.selectFork(_inkMainnetForkId); + _etchImplementations( + operatorFeeVaultImpl, + sequencerFeeVaultImpl, + defaultFeeVaultImpl, + feeSplitterImpl, + operatorFeeVaultImplCode, + sequencerFeeVaultImplCode, + defaultFeeVaultImplCode, + feeSplitterImplCode + ); + _setupProxyPredeploys( + proxyCode, operatorFeeVaultImpl, sequencerFeeVaultImpl, defaultFeeVaultImpl, feeSplitterImpl + ); + + // Switch back to mainnet fork after setup + vm.selectFork(_mainnetForkId); + } + + /// @notice Deploy a contract from creation code + /// @param _creationCode The creation code of the contract to deploy + /// @return deployed The address of the deployed contract + function _deployFromCreationCode(bytes memory _creationCode) internal returns (address deployed) { + assembly { + deployed := create(0, add(_creationCode, 0x20), mload(_creationCode)) + } + require(deployed != address(0), "Deployment failed"); + } + + /// @notice Etch implementation bytecode at addresses on the current fork + /// @param _operatorFeeVaultImpl OperatorFeeVault implementation address + /// @param _sequencerFeeVaultImpl SequencerFeeVault implementation address + /// @param _defaultFeeVaultImpl Default FeeVault implementation address + /// @param _feeSplitterImpl FeeSplitter implementation address + /// @param _operatorFeeVaultImplCode OperatorFeeVault implementation bytecode + /// @param _sequencerFeeVaultImplCode SequencerFeeVault implementation bytecode + /// @param _defaultFeeVaultImplCode Default FeeVault implementation bytecode + /// @param _feeSplitterImplCode FeeSplitter implementation bytecode + function _etchImplementations( + address _operatorFeeVaultImpl, + address _sequencerFeeVaultImpl, + address _defaultFeeVaultImpl, + address _feeSplitterImpl, + bytes memory _operatorFeeVaultImplCode, + bytes memory _sequencerFeeVaultImplCode, + bytes memory _defaultFeeVaultImplCode, + bytes memory _feeSplitterImplCode + ) internal { + vm.etch(_operatorFeeVaultImpl, _operatorFeeVaultImplCode); + vm.etch(_sequencerFeeVaultImpl, _sequencerFeeVaultImplCode); + vm.etch(_defaultFeeVaultImpl, _defaultFeeVaultImplCode); + vm.etch(_feeSplitterImpl, _feeSplitterImplCode); + } + + /// @notice Setup proxy predeploys pointing to implementations + /// @param _proxyCode Proxy runtime bytecode + /// @param _operatorFeeVaultImpl OperatorFeeVault implementation address + /// @param _sequencerFeeVaultImpl SequencerFeeVault implementation address + /// @param _defaultFeeVaultImpl Default FeeVault implementation address (for Base and L1) + /// @param _feeSplitterImpl FeeSplitter implementation address + function _setupProxyPredeploys( + bytes memory _proxyCode, + address _operatorFeeVaultImpl, + address _sequencerFeeVaultImpl, + address _defaultFeeVaultImpl, + address _feeSplitterImpl + ) internal { + // Setup OperatorFeeVault proxy + vm.etch(OPERATOR_FEE_VAULT, _proxyCode); + vm.store(OPERATOR_FEE_VAULT, PROXY_IMPLEMENTATION_SLOT, bytes32(uint256(uint160(_operatorFeeVaultImpl)))); + vm.store(OPERATOR_FEE_VAULT, PROXY_OWNER_SLOT, bytes32(uint256(uint160(RevShareCommon.PROXY_ADMIN)))); + + // Setup SequencerFeeVault proxy + vm.etch(SEQUENCER_FEE_VAULT, _proxyCode); + vm.store(SEQUENCER_FEE_VAULT, PROXY_IMPLEMENTATION_SLOT, bytes32(uint256(uint160(_sequencerFeeVaultImpl)))); + vm.store(SEQUENCER_FEE_VAULT, PROXY_OWNER_SLOT, bytes32(uint256(uint160(RevShareCommon.PROXY_ADMIN)))); + + // Setup BaseFeeVault proxy + vm.etch(BASE_FEE_VAULT, _proxyCode); + vm.store(BASE_FEE_VAULT, PROXY_IMPLEMENTATION_SLOT, bytes32(uint256(uint160(_defaultFeeVaultImpl)))); + vm.store(BASE_FEE_VAULT, PROXY_OWNER_SLOT, bytes32(uint256(uint160(RevShareCommon.PROXY_ADMIN)))); + + // Setup L1FeeVault proxy + vm.etch(L1_FEE_VAULT, _proxyCode); + vm.store(L1_FEE_VAULT, PROXY_IMPLEMENTATION_SLOT, bytes32(uint256(uint160(_defaultFeeVaultImpl)))); + vm.store(L1_FEE_VAULT, PROXY_OWNER_SLOT, bytes32(uint256(uint160(RevShareCommon.PROXY_ADMIN)))); + + // Setup FeeSplitter proxy + vm.etch(FEE_SPLITTER, _proxyCode); + vm.store(FEE_SPLITTER, PROXY_IMPLEMENTATION_SLOT, bytes32(uint256(uint160(_feeSplitterImpl)))); + vm.store(FEE_SPLITTER, PROXY_OWNER_SLOT, bytes32(uint256(uint160(RevShareCommon.PROXY_ADMIN)))); + } + + /// @notice Test the integration of setupRevShare + function test_setupRevShare_integration() public { + // Step 1: Record logs for L1→L2 message replay + vm.recordLogs(); + + // Step 2: Execute task simulation + revShareTask.simulate("test/tasks/example/eth/017-revshare-setup/config.toml"); + + // Step 3: Relay deposit transactions from L1 to all L2s + uint256[] memory forkIds = new uint256[](2); + forkIds[0] = _opMainnetForkId; + forkIds[1] = _inkMainnetForkId; + + address[] memory portals = new address[](2); + portals[0] = OP_MAINNET_PORTAL; + portals[1] = INK_MAINNET_PORTAL; + + _relayAllMessages(forkIds, IS_SIMULATE, portals); + + // Step 4: Assert the state of the OP Mainnet contracts + vm.selectFork(_opMainnetForkId); + _assertL2State( + OP_L1_WITHDRAWER, + OP_REV_SHARE_CALCULATOR, + OP_MIN_WITHDRAWAL_AMOUNT, + OP_L1_WITHDRAWAL_RECIPIENT, + OP_WITHDRAWAL_GAS_LIMIT, + OP_CHAIN_FEES_RECIPIENT + ); + + // Step 5: Assert the state of the Ink Mainnet contracts + vm.selectFork(_inkMainnetForkId); + _assertL2State( + INK_L1_WITHDRAWER, + INK_REV_SHARE_CALCULATOR, + INK_MIN_WITHDRAWAL_AMOUNT, + INK_L1_WITHDRAWAL_RECIPIENT, + INK_WITHDRAWAL_GAS_LIMIT, + INK_CHAIN_FEES_RECIPIENT + ); + + // Step 6: Do a withdrawal flow + + // Fund vaults with amount > minWithdrawalAmount + _fundVaults(1 ether, _opMainnetForkId); + _fundVaults(1 ether, _inkMainnetForkId); + + // Disburse fees in both chains and expect the L1Withdrawer to trigger the 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; + + _executeDisburseAndAssertWithdrawal(_opMainnetForkId, OP_L1_WITHDRAWAL_RECIPIENT, expectedWithdrawalAmount); + _executeDisburseAndAssertWithdrawal(_inkMainnetForkId, INK_L1_WITHDRAWAL_RECIPIENT, expectedWithdrawalAmount); + } +} diff --git a/test/tasks/Regression.t.sol b/test/tasks/Regression.t.sol index e5e4bd5cb..23160e739 100644 --- a/test/tasks/Regression.t.sol +++ b/test/tasks/Regression.t.sol @@ -42,6 +42,7 @@ import {OPCMUpgradeSuperchainConfigV410} from "src/template/OPCMUpgradeSuperchai import {L1PortalExecuteL2Call} from "src/template/L1PortalExecuteL2Call.sol"; import {AddGameTypeTemplate} from "src/template/AddGameTypeTemplate.sol"; import {RevShareUpgradeAndSetup} from "src/template/RevShareUpgradeAndSetup.sol"; +import {RevShareSetup} from "src/template/RevShareSetup.sol"; /// @notice Ensures that simulating the task consistently produces the same call data and data to sign. /// This guarantees determinism if a bug is introduced in the task logic, the call data or data to sign @@ -982,6 +983,63 @@ contract RegressionTest is Test { _assertDataToSignNestedMultisig(multisigTask, actions, expectedDataToSign, MULTICALL3_ADDRESS, rootSafe); } + /// @notice Expected call data and data to sign generated by manually running the RevShareSetup template at block 23820274 on mainnet. + /// Simulate from task directory (test/tasks/example/eth/017-revshare-setup) with: + /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path "$(pwd)"/.env --justfile ../../../../../src/justfile simulate + function testRegressionCallDataMatches_RevShareSetup() public { + string memory taskConfigFilePath = "test/tasks/example/eth/017-revshare-setup/config.toml"; + string memory expectedCallData = + "0x82ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000006fee65a372d63295e8eb12574652f0bfeb913149000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184cdab407800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000beb5fc579115071764c7423a4f12edde41f106ed0000000000000000000000000000000000000000000000000000000000055730000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005d66c1782664115999c47c9fa5cd031f495d3e4f000000000000000000000000000000000000000000000000000000000007a120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000"; + MultisigTask multisigTask = new RevShareSetup(); + address rootSafe = address(0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A); + address foundationChildMultisig = 0x847B5c174615B1B7fDF770882256e2D3E95b9D92; + address[] memory allSafes = MultisigTaskTestHelper.getAllSafes(rootSafe, foundationChildMultisig); + + (Action[] memory actions, uint256[] memory allOriginalNonces) = + _setupAndSimulate(taskConfigFilePath, 23820274, "mainnet", multisigTask, allSafes); + + _assertCallDataMatches(multisigTask, actions, allSafes, allOriginalNonces, expectedCallData); + + string[] memory expectedDataToSign = new string[](2); + + // Foundation + expectedDataToSign[0] = + "0x1901a4a9c312badf3fcaa05eafe5dc9bee8bd9316c78ee8b0bebe3115bb21b732672babdaa6c60c018aea833417700ce7e0a97768f0941586fcba368f0a8a874c277"; + // Security council + expectedDataToSign[1] = + "0x1901df53d510b56e539b90b369ef08fce3631020fbf921e3136ea5f8747c20bce96715194e3147a0c8a32ef87e4beed5390c7d4abea66d51a9863c04959bc09f4765"; + + _assertDataToSignNestedMultisig(multisigTask, actions, expectedDataToSign, MULTICALL3_ADDRESS, rootSafe); + } + + /// @notice Expected call data and data to sign generated by manually running the RevShareUpgradeAndSetup template at block 9628956 on Sepolia. + /// Simulate from task directory (test/tasks/example/sep/031-revshare-setup) with: + /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path $(pwd)/.env --justfile ../../../../../src/improvements/single.just simulate + function testRegressionCallDataMatches_RevShareSetupSepolia() public { + string memory taskConfigFilePath = "test/tasks/example/sep/031-revshare-setup/config.toml"; + string memory expectedCallData = + "0x82ad56cb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000890c61c7f3f40b851ebcaacfa879c6075426419d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4cdab40780000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000016fc5058f25648194471939df75cf27a2fdc48bc0000000000000000000000000000000000000000000000000000000000055730000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000c3500000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000"; + MultisigTask multisigTask = new RevShareSetup(); + address rootSafe = address(0x1Eb2fFc903729a0F03966B917003800b145F56E2); + address foundationChildMultisig = address(0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B); + address[] memory allSafes = MultisigTaskTestHelper.getAllSafes(rootSafe, foundationChildMultisig); + + (Action[] memory actions, uint256[] memory allOriginalNonces) = + _setupAndSimulate(taskConfigFilePath, 9628956, "sepolia", multisigTask, allSafes); + + _assertCallDataMatches(multisigTask, actions, allSafes, allOriginalNonces, expectedCallData); + + string[] memory expectedDataToSign = new string[](2); + // Foundation + expectedDataToSign[0] = + "0x190137e1f5dd3b92a004a23589b741196c8a214629d4ea3a690ec8e41ae45c689cbb60593f7cc9aeca00d93e8d3bc4ecfc1d1d3f77fd8e9d914002b75cf47d794d41"; + // Security Council + expectedDataToSign[1] = + "0x1901be081970e9fc104bd1ea27e375cd21ec7bb1eec56bfe43347c3e36c5d27b8533a12d8bcecba052629a3d85176a5924c80e5faa201eed78daf784b6d0cbbdf5e2"; + + _assertDataToSignNestedMultisig(multisigTask, actions, expectedDataToSign, MULTICALL3_ADDRESS, rootSafe); + } + /// @notice Expected call data and data to sign generated by manually running the RevShareUpgradeAndSetup template at block 23820274 on mainnet. /// Simulate from task directory (test/tasks/example/eth/016-revshare-upgrade-and-setup) with: /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path "$(pwd)"/.env --justfile ../../../../../src/justfile simulate diff --git a/test/tasks/example/eth/016-revshare-upgrade-and-setup/config.toml b/test/tasks/example/eth/016-revshare-upgrade-and-setup/config.toml index d130351fd..a97c00efc 100644 --- a/test/tasks/example/eth/016-revshare-upgrade-and-setup/config.toml +++ b/test/tasks/example/eth/016-revshare-upgrade-and-setup/config.toml @@ -17,15 +17,15 @@ portals = [ "0x5d66C1782664115999C47c9fA5cd031f495D3e4F" # Ink Mainnet Portal ] chainFeesRecipients = [ - "0x0000000000000000000000000000000000000001", # TODO: Update to the corresponding address once defined - "0x0000000000000000000000000000000000000002" # TODO: Update to the corresponding address once defined + "0x0000000000000000000000000000000000000001", # Update to the corresponding address once defined + "0x0000000000000000000000000000000000000002" # Update to the corresponding address once defined ] l1WithdrawerMinWithdrawalAmounts = [350000, 500000] l1WithdrawerRecipients = [ - # TODO: Update to the corresponding (e.g. FeesDepositor) address once defined + # TODO(17505): Update to the corresponding (e.g. FeesDepositor) address once defined "0x0000000000000000000000000000000000000001", - # TODO: Update to the corresponding (e.g. FeesDepositor) address once defined + # TODO(17505): Update to the corresponding (e.g. FeesDepositor) address once defined "0x0000000000000000000000000000000000000002" ] l1WithdrawerGasLimits = [800000, 1000000] diff --git a/test/tasks/example/eth/017-revshare-setup/.env b/test/tasks/example/eth/017-revshare-setup/.env new file mode 100644 index 000000000..861095467 --- /dev/null +++ b/test/tasks/example/eth/017-revshare-setup/.env @@ -0,0 +1,3 @@ +TENDERLY_GAS=16000000 +NESTED_SAFE_NAME_DEPTH_1=foundation +FORK_BLOCK_NUMBER=23820274 \ No newline at end of file diff --git a/test/tasks/example/eth/017-revshare-setup/config.toml b/test/tasks/example/eth/017-revshare-setup/config.toml new file mode 100644 index 000000000..a90b6664f --- /dev/null +++ b/test/tasks/example/eth/017-revshare-setup/config.toml @@ -0,0 +1,34 @@ +# RevShare Setup Configuration + +templateName = "RevShareSetup" + +# L2 chains to target +l2chains = [{name = "OP Mainnet", chainId = 10}, {name = "Ink", chainId = 57073}] + +safeAddress = "ProxyAdminOwner" + +# RevShareContractsUpgrader address +# TODO: Update to the corresponding address after audit +revShareUpgrader = "0x6fee65A372d63295E8EB12574652F0BfEb913149" + +# RevShare configurations (one per chain) - flattened arrays +portals = [ + "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed", # OP Mainnet Portal + "0x5d66C1782664115999C47c9fA5cd031f495D3e4F" # Ink Mainnet Portal +] +chainFeesRecipients = [ + "0x0000000000000000000000000000000000000001", # Update to the corresponding address once defined + "0x0000000000000000000000000000000000000002" # Update to the corresponding address once defined +] +l1WithdrawerMinWithdrawalAmounts = [350000, 500000] + +l1WithdrawerRecipients = [ + # TODO(17505): Update to the corresponding (e.g. FeesDepositor) address once defined + "0x0000000000000000000000000000000000000001", + # TODO(17505): Update to the corresponding (e.g. FeesDepositor) address once defined + "0x0000000000000000000000000000000000000002" +] +l1WithdrawerGasLimits = [800000, 1000000] + +[addresses] +ProxyAdminOwner = "0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A" diff --git a/test/tasks/example/sep/031-revshare-setup/.env b/test/tasks/example/sep/031-revshare-setup/.env new file mode 100644 index 000000000..2fd241eab --- /dev/null +++ b/test/tasks/example/sep/031-revshare-setup/.env @@ -0,0 +1,3 @@ +TENDERLY_GAS=16000000 +FORK_BLOCK_NUMBER=9628956 +NESTED_SAFE_NAME_DEPTH_1=foundation diff --git a/test/tasks/example/sep/031-revshare-setup/config.toml b/test/tasks/example/sep/031-revshare-setup/config.toml new file mode 100644 index 000000000..e39def724 --- /dev/null +++ b/test/tasks/example/sep/031-revshare-setup/config.toml @@ -0,0 +1,22 @@ +# RevShare Setup Configuration + +templateName = "RevShareSetup" + +# L2 chains to target +l2chains = [{name = "OP Sepolia Testnet", chainId = 11155420}] + +safeAddress = "ProxyAdminOwner" + +# RevShareContractsUpgrader address +# TODO: Update to the corresponding address after audit +revShareUpgrader = "0x890C61C7F3f40B851EbCAacFA879C6075426419D" + +# RevShare configurations (one per chain) - flattened arrays +portals = ["0x16Fc5058F25648194471939df75CF27A2fdC48BC"] # OP Sepolia Testnet Portal +chainFeesRecipients = ["0x0000000000000000000000000000000000000001"] # OP Sepolia Testnet chain fees recipient +l1WithdrawerMinWithdrawalAmounts = [350000] +l1WithdrawerRecipients = ["0x0000000000000000000000000000000000000001"] +l1WithdrawerGasLimits = [800000] + +[addresses] +ProxyAdminOwner = "0x1Eb2fFc903729a0F03966B917003800b145F56E2"