Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
8957282
feat: add rev share upgrader
0xChin Nov 4, 2025
f081a19
feat: add upgrade function
0xChin Nov 4, 2025
d0f52d6
chore: remove wrong checks
0xChin Nov 5, 2025
7910b0c
feat: add combined path
0xChin Nov 5, 2025
bb918b0
refactor: remove no sense struct
0xChin Nov 5, 2025
dcd596d
chore: fix comments
0xChin Nov 5, 2025
6f11b32
feat: add templates with example configs
0xChin Nov 5, 2025
1c7c070
chore: descope
0xChin Nov 5, 2025
29b0cc7
refactor: move interfaces to single files
0xChin Nov 6, 2025
64a4838
docs: natspec
0xChin Nov 6, 2025
f628e32
refactor: use custom errors
0xChin Nov 6, 2025
90ffdea
chore: improve comments
0xChin Nov 6, 2025
495ecb1
fix: reorder contracts deployment
0xChin Nov 6, 2025
96acf0b
chore: improve function naming
0xChin Nov 6, 2025
4650303
chore: improve function naming
0xChin Nov 6, 2025
80d93a1
chore: improve comments
0xChin Nov 6, 2025
4fb8591
chore(descope): remove upgradeContracts function
0xChin Nov 7, 2025
8378bb9
chore(descope): hardcode saltSeed
0xChin Nov 7, 2025
2750349
chore(descope): hardcode same salt for all contracts
0xChin Nov 7, 2025
21e825a
feat: allow multiple chain upgrades at once goooood
0xChin Nov 7, 2025
30d8d7d
test: add integration test
0xChin Nov 7, 2025
77965ec
fix: colission due to default salt
0xChin Nov 7, 2025
ee1d78b
test: add integration test with delegatecall
0xChin Nov 7, 2025
1524459
perf: remove default variable init
0xChin Nov 7, 2025
528e60b
refactor: remove magic number usage
0xChin Nov 7, 2025
e8b4b3b
refactor: use helper functions for portal deposits
0xChin Nov 7, 2025
6662636
refactor: move main case to beginning
0xChin Nov 7, 2025
dd34193
refactor: improve var naming
0xChin Nov 7, 2025
5fe8f70
chore: add zero length array check
0xChin Nov 7, 2025
4200f56
refactor: add deploy periphery contract
0xChin Nov 7, 2025
34855bb
refactor: invert calls order
0xChin Nov 7, 2025
111c49a
refactor: reduce interface complexity
0xChin Nov 7, 2025
a76d1f0
feat: add revshare template and integration tests
0xChin Nov 10, 2025
88a9d39
refactor: remove fixture file
0xChin Nov 10, 2025
8c3599b
refactor: cleanup
0xChin Nov 10, 2025
93542ad
chore: improve naming
0xChin Nov 10, 2025
03d4cfa
feat: add portal check
0xChin Nov 10, 2025
d61043c
chore: remove unused task
0xChin Nov 10, 2025
582d73b
refactor: use interfaces
0xChin Nov 10, 2025
acaaf16
chore: improve readability
0xChin Nov 10, 2025
d69bf10
chore: fix contract name
0xChin Nov 10, 2025
a5f713f
feat: fix contract naming, add regression tests
0xChin Nov 10, 2025
1e674a4
test: add todo regression
0xChin Nov 10, 2025
4c3e55e
test: make integration tests with different configs for both chains
0xChin Nov 10, 2025
3079543
chore: remove regression tests until upgrader is deployed
0xChin Nov 10, 2025
616fb00
chore: remove deprecated tasks and tests
0xChin Nov 10, 2025
0f59f65
chore: remove deprecated test
0xChin Nov 10, 2025
6a015e9
refactor: use common library for all revshare configs/data/creation c…
0xChin Nov 10, 2025
1dad4b4
chore: remove default value initalization
0xChin Nov 10, 2025
bd5a0fb
refactor: rename RevShareManager -> RevShareUpgrader
0xChin Nov 11, 2025
585bee0
refactor: single param for calling upgrader contract
0xChin Nov 11, 2025
d9a62b8
refactor: remove portals array from template config
0xChin Nov 11, 2025
c02d294
refactor: remove unnecessary encoding and decoding
0xChin Nov 11, 2025
2d7e5af
refactor: remove unnecessary for loop
0xChin Nov 11, 2025
4baf448
fix: broken validation
0xChin Nov 11, 2025
ef6f900
refactor: improve var naming
0xChin Nov 11, 2025
7c143e0
refactor: improve var naming
0xChin Nov 11, 2025
617a779
feat: improve template validations
0xChin Nov 11, 2025
491dfc7
refactor: remove assembly usage
0xChin Nov 11, 2025
6e57f0c
feat: use same impl for BaseFeeVault and L1FeeVault
0xChin Nov 11, 2025
a932638
chore: improve comments
0xChin Nov 11, 2025
ec1d538
refactor: use for loop for vaults deployment
0xChin Nov 11, 2025
e83ae80
feat: add upgrader unit tests (#55)
0xDiscotech Nov 11, 2025
99a3551
chore: add comment
0xDiscotech Nov 11, 2025
b532af6
feat: add check and interface (#56)
0xDiscotech Nov 11, 2025
ad2dc9d
chore: fix gas costs
0xChin Nov 11, 2025
f2d108b
chore: undo wrong changes
0xChin Nov 11, 2025
1bdc236
chore: update gas limits (#57)
0xChin Nov 11, 2025
ce3936c
feat: add chain processed event (#58)
0xDiscotech Nov 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions src/RevShareContractsUpgrader.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {RevShareLibrary} from "src/libraries/RevShareLibrary.sol";
import {Utils} from "src/libraries/Utils.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";

/// @title RevShareContractsUpgrader
/// @notice Upgrader contract that manages RevShare deployments and configuration via delegatecall.
/// @dev Supports two operations:
/// 1. setupRevShare() - Setup revenue sharing on already-upgraded contracts
/// 2. upgradeAndSetupRevShare() - Combined upgrade + setup (most efficient)
/// All operations use the default calculator (L1Withdrawer + SuperchainRevenueShareCalculator).
contract RevShareContractsUpgrader {
/// @notice Base salt seed for CREATE2 deployments
string private constant SALT_SEED = "RevShare";

/// @notice Thrown when portal address is zero
error PortalCannotBeZeroAddress();

/// @notice Thrown when L1Withdrawer recipient is zero address
error L1WithdrawerRecipientCannotBeZeroAddress();

/// @notice Thrown when chain fees recipient is zero address
error ChainFeesRecipientCannotBeZeroAddress();

/// @notice Thrown when gas limit is zero
error GasLimitCannotBeZero();

/// @notice Thrown when array is empty
error EmptyArray();

/// @notice Emitted when a chain's RevShare setup deposits are completed
/// @param portal The portal address for the chain
/// @param chainIndex The index of the chain in the configs array
event ChainProcessed(address portal, uint256 chainIndex);

/// @notice Struct for L1Withdrawer configuration.
/// @param minWithdrawalAmount Minimum withdrawal amount
/// @param recipient Recipient address for withdrawals
/// @param gasLimit Gas limit for L1 withdrawals
struct L1WithdrawerConfig {
uint256 minWithdrawalAmount;
address recipient;
uint32 gasLimit;
}

/// @notice Struct for RevShare setup configuration per chain.
/// @param portal OptimismPortal2 address for the target L2
/// @param l1WithdrawerConfig L1Withdrawer configuration
/// @param chainFeesRecipient Chain fees recipient address for the calculator
struct RevShareConfig {
address portal;
L1WithdrawerConfig l1WithdrawerConfig;
address chainFeesRecipient;
}

/// @notice Upgrades vault and splitter contracts and sets up revenue sharing in one transaction for multiple chains.
/// This is the most efficient path as vaults are initialized with RevShare config from the start.
/// @param _configs Array of RevShare configuration structs, one per chain.
function upgradeAndSetupRevShare(RevShareConfig[] calldata _configs) external {
if (_configs.length == 0) revert EmptyArray();

for (uint256 i; i < _configs.length; i++) {
RevShareConfig calldata config = _configs[i];
if (config.portal == address(0)) revert PortalCannotBeZeroAddress();
if (config.l1WithdrawerConfig.recipient == address(0)) revert L1WithdrawerRecipientCannotBeZeroAddress();
if (config.chainFeesRecipient == address(0)) revert ChainFeesRecipientCannotBeZeroAddress();
if (config.l1WithdrawerConfig.gasLimit == 0) revert GasLimitCannotBeZero();

// Deploy L1Withdrawer and SuperchainRevenueShareCalculator
address precalculatedCalculator =
_deployRevSharePeriphery(config.portal, config.l1WithdrawerConfig, config.chainFeesRecipient);

// Upgrade fee splitter and initialize with calculator FIRST
// This prevents the edge case where fees could be sent to an uninitialized FeeSplitter
bytes32 feeSplitterSalt = _getSalt("FeeSplitter");
address feeSplitterImpl = Utils.getCreate2Address(
feeSplitterSalt, RevShareLibrary.feeSplitterCreationCode, RevShareLibrary.CREATE2_DEPLOYER
);
_depositCreate2(
config.portal,
RevShareLibrary.FEE_SPLITTER_DEPLOYMENT_GAS_LIMIT,
feeSplitterSalt,
RevShareLibrary.feeSplitterCreationCode
);
_depositCall(
config.portal,
address(RevShareLibrary.PROXY_ADMIN),
RevShareLibrary.UPGRADE_GAS_LIMIT,
abi.encodeCall(
IProxyAdmin.upgradeAndCall,
(
payable(RevShareLibrary.FEE_SPLITTER),
feeSplitterImpl,
abi.encodeCall(IFeeSplitter.initialize, (precalculatedCalculator))
)
)
);

// Upgrade all 4 vaults with RevShare configuration (recipient=FeeSplitter, minWithdrawal=0, network=L2)
_upgradeVaultsWithRevShareConfig(config.portal);

emit ChainProcessed(config.portal, i);
}
}

/// @notice Enables revenue sharing after vaults have been upgraded and `FeeSplitter` initialized.
/// Deploys L1Withdrawer and calculator, then configures vaults and splitter for multiple chains.
/// @param _configs Array of RevShare configuration structs, one per chain.
function setupRevShare(RevShareConfig[] calldata _configs) external {
if (_configs.length == 0) revert EmptyArray();

for (uint256 i; i < _configs.length; i++) {
RevShareConfig calldata config = _configs[i];
if (config.portal == address(0)) revert PortalCannotBeZeroAddress();
if (config.l1WithdrawerConfig.recipient == address(0)) revert L1WithdrawerRecipientCannotBeZeroAddress();
if (config.chainFeesRecipient == address(0)) revert ChainFeesRecipientCannotBeZeroAddress();
if (config.l1WithdrawerConfig.gasLimit == 0) revert GasLimitCannotBeZero();

// Deploy L1Withdrawer and SuperchainRevenueShareCalculator
address calculator =
_deployRevSharePeriphery(config.portal, config.l1WithdrawerConfig, config.chainFeesRecipient);

// Set calculator on fee splitter
_depositCall(
config.portal,
RevShareLibrary.FEE_SPLITTER,
RevShareLibrary.SETTERS_GAS_LIMIT,
abi.encodeCall(IFeeSplitter.setSharesCalculator, (calculator))
);

// Configure all 4 vaults for revenue sharing
_configureVaultsForRevShare(config.portal);

emit ChainProcessed(config.portal, i);
}
}

/// @notice Deploys L1Withdrawer and SuperchainRevenueShareCalculator to L2.
/// @param _portal The OptimismPortal2 address for the target L2
/// @param _l1WithdrawerConfig L1Withdrawer configuration
/// @param _chainFeesRecipient Chain fees recipient address
/// @return calculator The deployed calculator address
function _deployRevSharePeriphery(
address _portal,
L1WithdrawerConfig calldata _l1WithdrawerConfig,
address _chainFeesRecipient
) private returns (address calculator) {
// Deploy L1Withdrawer
bytes memory l1WithdrawerInitCode = bytes.concat(
RevShareLibrary.l1WithdrawerCreationCode,
abi.encode(
_l1WithdrawerConfig.minWithdrawalAmount, _l1WithdrawerConfig.recipient, _l1WithdrawerConfig.gasLimit
)
);
bytes32 l1WithdrawerSalt = _getSalt("L1Withdrawer");
address precalculatedL1Withdrawer =
Utils.getCreate2Address(l1WithdrawerSalt, l1WithdrawerInitCode, RevShareLibrary.CREATE2_DEPLOYER);
_depositCreate2(
_portal, RevShareLibrary.L1_WITHDRAWER_DEPLOYMENT_GAS_LIMIT, l1WithdrawerSalt, l1WithdrawerInitCode
);

// Deploy SuperchainRevenueShareCalculator
bytes memory calculatorInitCode = bytes.concat(
RevShareLibrary.scRevShareCalculatorCreationCode, abi.encode(precalculatedL1Withdrawer, _chainFeesRecipient)
);
bytes32 calculatorSalt = _getSalt("SCRevShareCalculator");
calculator = Utils.getCreate2Address(calculatorSalt, calculatorInitCode, RevShareLibrary.CREATE2_DEPLOYER);
_depositCreate2(
_portal, RevShareLibrary.SC_REV_SHARE_CALCULATOR_DEPLOYMENT_GAS_LIMIT, calculatorSalt, calculatorInitCode
);
}

/// @notice Configures all 4 vaults for revenue sharing (recipient=FeeSplitter, minWithdrawal=0, network=L2).
/// @param _portal The OptimismPortal2 address for the target L2
function _configureVaultsForRevShare(address _portal) private {
address[4] memory vaults = [
RevShareLibrary.OPERATOR_FEE_VAULT,
RevShareLibrary.SEQUENCER_FEE_WALLET,
RevShareLibrary.BASE_FEE_VAULT,
RevShareLibrary.L1_FEE_VAULT
];

for (uint256 i; i < vaults.length; i++) {
_depositCall(
_portal,
vaults[i],
RevShareLibrary.SETTERS_GAS_LIMIT,
abi.encodeCall(IFeeVault.setRecipient, (RevShareLibrary.FEE_SPLITTER))
);
_depositCall(
_portal,
vaults[i],
RevShareLibrary.SETTERS_GAS_LIMIT,
abi.encodeCall(IFeeVault.setMinWithdrawalAmount, (0))
);
_depositCall(
_portal,
vaults[i],
RevShareLibrary.SETTERS_GAS_LIMIT,
abi.encodeCall(IFeeVault.setWithdrawalNetwork, (IFeeVault.WithdrawalNetwork.L2))
);
}
}

/// @notice Upgrades all 4 vaults with RevShare configuration (recipient=FeeSplitter, minWithdrawal=0, network=L2).
/// Deploys only 3 implementations: OperatorFeeVault, SequencerFeeVault, and the same FeeVault implementation is used for both BaseFeeVault and L1FeeVault (we use the same one for both to avoid making the deployment size bigger).
/// @param _portal The OptimismPortal2 address for the target L2
function _upgradeVaultsWithRevShareConfig(address _portal) private {
address[4] memory vaultProxies = [
RevShareLibrary.OPERATOR_FEE_VAULT,
RevShareLibrary.SEQUENCER_FEE_WALLET,
RevShareLibrary.BASE_FEE_VAULT,
RevShareLibrary.L1_FEE_VAULT
];
bytes[4] memory creationCodes = [
RevShareLibrary.operatorFeeVaultCreationCode,
RevShareLibrary.sequencerFeeVaultCreationCode,
RevShareLibrary.defaultFeeVaultCreationCode,
RevShareLibrary.defaultFeeVaultCreationCode
];
string[4] memory vaultNames = ["OperatorFeeVault", "SequencerFeeVault", "BaseFeeVault", "L1FeeVault"];

address defaultImpl;
for (uint256 i; i < vaultProxies.length; i++) {
bytes32 salt = _getSalt(vaultNames[i]);
address impl;

// Check if this is the BaseFeeVault or L1FeeVault (both use default implementation)
bool isBaseFeeVault = keccak256(bytes(vaultNames[i])) == keccak256(bytes("BaseFeeVault"));
bool isL1FeeVault = keccak256(bytes(vaultNames[i])) == keccak256(bytes("L1FeeVault"));

if (isBaseFeeVault) {
// Deploy default implementation for BaseFeeVault
impl = Utils.getCreate2Address(salt, creationCodes[i], RevShareLibrary.CREATE2_DEPLOYER);
defaultImpl = impl;
_depositCreate2(_portal, RevShareLibrary.FEE_VAULTS_DEPLOYMENT_GAS_LIMIT, salt, creationCodes[i]);
} else if (isL1FeeVault) {
// Reuse the default implementation for L1FeeVault
impl = defaultImpl;
} else {
// Deploy specific implementations for OperatorFeeVault and SequencerFeeVault
impl = Utils.getCreate2Address(salt, creationCodes[i], RevShareLibrary.CREATE2_DEPLOYER);
_depositCreate2(_portal, RevShareLibrary.FEE_VAULTS_DEPLOYMENT_GAS_LIMIT, salt, creationCodes[i]);
}
Comment on lines +216 to +252
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The readibility here got a bit worse because we decided to only deploy one implementation FeeVault for both BaseFeeVault and L1FeeVault given they use the exact same bytecode.
On the previous way, the loop was way cleaner but it required an extra deployment, something that is not trivial given that the accumulation of deposits on the block are expensive in terms on gas, and we have a limit there (usually 20m gas).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious about wdyt about this @funkornaut001 @0xng

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to me as is. Previously, with the four deployments, were you approaching the block gas limit?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were deploying four vaults, one per each, even though BaseFeeVault and L1FeeVault share the same one

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's ok, but it's incohesive. Not the code itself, but the practice. I find two things off about this approach:

  1. The implementation is being deployed with the salt corresponding to the BaseFeeVault. This is awkward for the "L1FeeVault" in terms of following the practices. There's no real reason why it uses the salt of that vault over the other.
  2. The only reason this works is because they share the same semver version constant. Otherwise, their bytecodes would differ.
    Does any of these two points matter in practice? Not really. Point 2 could be an "issue" if this contract was meant to be reusable as the contracts evolved, because the bytecodes could technically differ at some point. But that's not the case, so fair game.


_depositCall(
_portal,
address(RevShareLibrary.PROXY_ADMIN),
RevShareLibrary.UPGRADE_GAS_LIMIT,
abi.encodeCall(
IProxyAdmin.upgradeAndCall,
(
payable(vaultProxies[i]),
impl,
abi.encodeCall(
IFeeVault.initialize, (RevShareLibrary.FEE_SPLITTER, 0, IFeeVault.WithdrawalNetwork.L2)
)
)
)
);
}
}

/// @notice Helper for CREATE2 contract deployments via depositTransaction.
/// @param _portal The OptimismPortal2 address
/// @param _gasLimit Gas limit for the transaction
/// @param _salt CREATE2 salt
/// @param _initCode Contract creation code with constructor args
function _depositCreate2(address _portal, uint64 _gasLimit, bytes32 _salt, bytes memory _initCode) private {
IOptimismPortal2(payable(_portal)).depositTransaction(
address(RevShareLibrary.CREATE2_DEPLOYER),
0,
_gasLimit,
false,
abi.encodeCall(ICreate2Deployer.deploy, (0, _salt, _initCode))
);
}

/// @notice Helper for regular function calls via depositTransaction.
/// @param _portal The OptimismPortal2 address
/// @param _target Target contract address
/// @param _gasLimit Gas limit for the transaction
/// @param _data Encoded function call data
function _depositCall(address _portal, address _target, uint64 _gasLimit, bytes memory _data) private {
IOptimismPortal2(payable(_portal)).depositTransaction(_target, 0, _gasLimit, false, _data);
}

/// @notice Generates a unique salt for CREATE2 deployments based on the contract suffix.
/// @param _suffix The suffix to append to the base salt seed
/// @return The generated salt as bytes32
function _getSalt(string memory _suffix) private pure returns (bytes32) {
return keccak256(abi.encodePacked(SALT_SEED, ":", _suffix));
}
}
7 changes: 7 additions & 0 deletions src/interfaces/ICreate2Deployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

/// @notice Interface of the Create2 Preinstall in L2.
interface ICreate2Deployer {
function deploy(uint256 _value, bytes32 _salt, bytes memory _code) external;
}
9 changes: 9 additions & 0 deletions src/interfaces/IFeeSplitter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

/// @notice Interface for the FeeSplitter in L2.
interface IFeeSplitter {
function initialize(address _sharesCalculator) external;
function sharesCalculator() external view returns (address);
function setSharesCalculator(address _calculator) external;
}
22 changes: 22 additions & 0 deletions src/interfaces/IFeeVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

interface IFeeVault {
enum WithdrawalNetwork {
L1,
L2
}

function initialize(address _recipient, uint256 _minWithdrawalAmount, WithdrawalNetwork _withdrawalNetwork) external;

function RECIPIENT() external view returns (address);
function MIN_WITHDRAWAL_AMOUNT() external view returns (uint256);
function WITHDRAWAL_NETWORK() external view returns (WithdrawalNetwork);
function minWithdrawalAmount() external view returns (uint256);
function recipient() external view returns (address);
function withdrawalNetwork() external view returns (WithdrawalNetwork);

function setRecipient(address _recipient) external;
function setMinWithdrawalAmount(uint256 _minWithdrawalAmount) external;
function setWithdrawalNetwork(WithdrawalNetwork _withdrawalNetwork) external;
}
8 changes: 8 additions & 0 deletions src/interfaces/IL1Withdrawer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

interface IL1Withdrawer {
function minWithdrawalAmount() external view returns (uint256);
function recipient() external view returns (address);
function withdrawalGasLimit() external view returns (uint32);
}
27 changes: 27 additions & 0 deletions src/interfaces/IRevShareContractsUpgrader.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IRevShareContractsUpgrader {
error PortalCannotBeZeroAddress();
error L1WithdrawerRecipientCannotBeZeroAddress();
error ChainFeesRecipientCannotBeZeroAddress();
error GasLimitCannotBeZero();
error EmptyArray();

event ChainProcessed(address portal, uint256 chainIndex);

struct L1WithdrawerConfig {
uint256 minWithdrawalAmount;
address recipient;
uint32 gasLimit;
}

struct RevShareConfig {
address portal;
L1WithdrawerConfig l1WithdrawerConfig;
address chainFeesRecipient;
}

function upgradeAndSetupRevShare(RevShareConfig[] calldata _configs) external;
function setupRevShare(RevShareConfig[] calldata _configs) external;
}
7 changes: 7 additions & 0 deletions src/interfaces/ISuperchainRevSharesCalculator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

interface ISuperchainRevSharesCalculator {
function shareRecipient() external view returns (address payable);
function remainderRecipient() external view returns (address payable);
}
Loading