From 360ef34c918468f42e4622366ca678e5dd0b1dd9 Mon Sep 17 00:00:00 2001 From: agusduha Date: Mon, 25 Aug 2025 17:26:30 -0300 Subject: [PATCH 1/9] feat: add fee splitter --- protocol/fee-splitter.md | 152 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 protocol/fee-splitter.md diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md new file mode 100644 index 00000000..b8c32414 --- /dev/null +++ b/protocol/fee-splitter.md @@ -0,0 +1,152 @@ +# Fee splitter + +| | | +| ------------------ | -------------------- | +| Author | _0xDiscotech_ | +| Created at | _2025-08-21_ | +| Initial Reviewers | _Tynes, Agus, Joxes_ | +| Need Approval From | _Tynes, Agus, Joxes_ | +| Status | _Draft_ | + +## Purpose + +Enabling both the Superchain and OP Stack chain operators to manage their revenue in an easy to configure and transparent way will help to enable sustainable onchain businesses. The goals of this design doc are to enshrine fee split contracts as predeploys so that any existing or future OP Stack chain can easily benefit from battle tested fee split logic. + +## Summary + +`FeeSplitter` aggregates fees from all the `FeeVault`s on L2, enforces the configured checks, and then disburses funds to recipients based on shares computed by a pluggable `SharesCalculator`. + +The calculator returns an array of disbursements—each with a `recipient`, `amount`—and `FeeSplitter` iterates through this list to execute each payout as a direct transfer to each recipient. + +## Problem Statement + Context + +Fixed fee shares are too rigid: chains want custom splits, L1/L2 destinations, and more complex policies. We need: + +- A safe, permissioned way to plug in chain‑specific logic for revenue share calculation. +- A stable, minimal surface in `FeeSplitter` to keep operations simple. +- Compatible with existing `FeeVault`s ABI. + +## Proposed Solution + +![Diagram.png](https://file.notion.so/f/f/7683bccd-1174-4689-a817-b27fd9d7ef00/c6217459-59b2-4c32-bf56-7a028ed3bdcf/Screenshot_2025-08-25_at_13.47.02.png?table=block&id=25a9a4c0-92c7-80c2-a19a-da6dc330687b&spaceId=7683bccd-1174-4689-a817-b27fd9d7ef00&expirationTimestamp=1756180800000&signature=OiiGbCy0ms4eZ9_XAVYv29K6D3dErx8LXOH-T0ZlwJs&downloadName=Screenshot+2025-08-25+at+13.47.02.png) + +The `FeeSplitter` will be a predeploy with a modular config. The `SharesCalculator` and each `Recipient` are external entities that integrate into the system. + +High‑level flow: + +1. Somebody (permissionless) calls `FeeSplitter.disburseFees()`. +2. Checks if the disbursement interval has passed. +3. Checks vault configs, pulls funds from vaults (if they reached the min withdrawal amount threshold), and computes per‑vault collected fees for granularity. +4. It calls the chain‑configured `SharesCalculator` with the revenue per vault as input to compute disbursements. +5. It receives the return data from `SharesCalculator`, validates outputs, stores the metadata on storage to make it externally accessible, and then transfers the amount to each recipient. + +**Invariants:** + +- On misconfigured vaults, `disbursementFees()` MUST revert +- On `SharesCalculator` returning either a wrong (e.g. sum mismatch) or malformed output, `disburseFees` MUST revert. +- On a recipient's payout failure, `disburseFees` MUST revert the entire transaction. +- On no funds, `disburseFees` MUST revert and MUST NOT consume the disbursement interval. +- When not on a disbursement context, `receive` MUST revert (`FeeVault.withdraw()` MUST revert if not call from the `FeeSplitter` when configured as its recipient). + +## `SuperchainRevShareCalculator` + +The `SuperchainRevShareCalculator` will be a `SharesCalculator` following the interface. Any contract that complies with the `ISharesCalculator` interface can be used to calculate the splits. Only the proxy admin owner will be able to set it. + +```solidity +interface ISharesCalculator { + function getSharesRecipients( + uint256 _sequencerFeeVaultRevenue, + uint256 _baseFeeVaultRevenue, + uint256 _operatorFeeVaultRevenue, + uint256 _l1FeeVaultRevenue + ) + external + view + returns (SharesRecipient[] memory recipients); +} + +struct SharesRecipient { + address payable recipient; + uint256 value; +} +``` + +It will split the fees between 2 recipients: `revenueShareRecipient` and `remainderRecipient`. + +To calculate the share to send to the `revenueShareRecpient`, it will get the `grossRevenue` (the sum of all vaults revenue) and the `netRevenue` (fees collected only from `SequencerFeeVault`, `OperatorFeeVault`, and `BaseFeeVault`). Then, the amount to transfer to it will be the higher value between 2.5% of the `grossRevenue` and 15% of the `netRevenue` — setting the remaining balance as the amount to transfer to `remainderRecipient`. + +### Resource Usage + +- Compared to current “vault → L1 withdrawal” flows, this adds an extra step at disbursement time with the `FeeSplitter` as intermediate, which incurs higher gas consumption. +- The `FeeSplitter` withdraw from the four vaults in the same transaction but it also add external calls from each of them to check their correct configuration. +- Admin setter `setShareCalculator` is trivial (single storage write + event) but adds some deployment size cost. + +### Single Point of Failure and Multi-Client Considerations + +- No client (op‑geth/op‑reth) changes needed. +- Opt‑in and per‑chain selectable calculator. The `FeeSplitter` surface remains stable and has an impact only on the app layer. + +## Failure Mode Analysis + +- DoS on a very big recipients array returned by the calculator, leading to a large number of disbursements and OOG +- DoS due to very high gas consumption by a recipient ending in OOG +- Misconfigured recipients that don't allow receiving the fees from the splitter, leading to a failed disbursement. +- Buggy `SharesCalculator` that returns incorrect or malformed outputs, leading to a failed disbursement. + +## Impact on Developer Experience + +- Chains get a clean plug‑in point to customize revshare without forking `FeeSplitter`. +- Recipients don’t need to implement a new interface; we continue using `SafeCall.send()`. + +## Alternatives Considered + +- Not tracking per vault revenue, but keep using the gross and net revenue approach to keep consistent with the previous version, but losing granularity that could be useful on more complex revenue share calculations. +- Supporting a `_metadata` return value from the `SharesCalculator` and storing it so it can be externally accessed by the recipients on a disbursement context. It can help make the solution more generic and future-proof, but it adds complexity, and we see no clear use case for now. +- Transfer to L2 or withdraw to L1 based on a `withdrawalNetwork` value received from the `SharesCalculator` per recipient on the `FeeSplitter`. + +## Risks & Uncertainties + +- If a calculator misbehaves (sum mismatch or downstream recipients revert), disbursements will revert. We’ll validate outputs and keep calculator swaps gated by `ProxyAdmin.owner()`. + +## Appendix + +### Appendix A: Disbursement Flow + +When `disburseFees()` is called, its sole responsibility is to handle the successive steps required to distribute funds to the recipients defined in the `SharesCalculator`. The flow depends on the configuration of each participant, and the entire call may revert if the required conditions are not met. + +```mermaid +sequenceDiagram + actor U as User + participant FS as FeeSplitter + participant FV as S/B/O/L1 FeeVault + participant SC as SharesCalculator + participant L2R as L2 Recipient + participant L1W as L1 Withdrawer + + U->>FS: disburseFees() + FS->>FS: checkDisbursementInterval() + FS->>FS: validateVaultConfigs() + + FS->>FV: withdraw() + + alt No funds / below minWithdrawalAmount + FS->>FS: revert("NoFundsOrBelowThreshold") + end + + FS->>FS: computePerVaultRevenues() + FS->>SC: getSharesRecipients(sequencer, base, operator, l1) + SC-->>FS: SharesRecipient[] (recipient, value) + FS->>FS: validateOutputs(sum==total && recipients>0) + alt Calculator revert / malformed output / sum mismatch + FS->>FS: revert("BadCalculatorOutput") + end + + FS->>L2R: send(value) (direct L2 transfer) + FS->>L1W: send(value) (route for L1 withdrawal) + + + alt Recipient rejects payment / OOG / bad logic + FS->>FS: revert("RecipientFailure") + end + +``` From 00927d71625ee6a47b79b3708b4c464ec18f4ae4 Mon Sep 17 00:00:00 2001 From: agusduha Date: Wed, 27 Aug 2025 12:46:06 -0300 Subject: [PATCH 2/9] fix: fix image --- protocol/fee-splitter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index b8c32414..5c89d9c9 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -28,7 +28,7 @@ Fixed fee shares are too rigid: chains want custom splits, L1/L2 destinations, a ## Proposed Solution -![Diagram.png](https://file.notion.so/f/f/7683bccd-1174-4689-a817-b27fd9d7ef00/c6217459-59b2-4c32-bf56-7a028ed3bdcf/Screenshot_2025-08-25_at_13.47.02.png?table=block&id=25a9a4c0-92c7-80c2-a19a-da6dc330687b&spaceId=7683bccd-1174-4689-a817-b27fd9d7ef00&expirationTimestamp=1756180800000&signature=OiiGbCy0ms4eZ9_XAVYv29K6D3dErx8LXOH-T0ZlwJs&downloadName=Screenshot+2025-08-25+at+13.47.02.png) +![Diagram.png](https://img.notionusercontent.com/s3/prod-files-secure%2F7683bccd-1174-4689-a817-b27fd9d7ef00%2Fc6217459-59b2-4c32-bf56-7a028ed3bdcf%2FScreenshot_2025-08-25_at_13.47.02.png/size/w=1420?exp=1756395886&sig=fObiioU1Qwbf4ySWEiN7Mzyf5Fr86Yimtr3DJgKZZE0&id=25a9a4c0-92c7-80c2-a19a-da6dc330687b&table=block) The `FeeSplitter` will be a predeploy with a modular config. The `SharesCalculator` and each `Recipient` are external entities that integrate into the system. From 37f19ffbae114771ef434dcfb4787a95ea600b82 Mon Sep 17 00:00:00 2001 From: Joxes <91908708+Joxess@users.noreply.github.com> Date: Mon, 1 Sep 2025 13:29:07 -0300 Subject: [PATCH 3/9] docs: improve purpose section Signed-off-by: Joxes <91908708+Joxess@users.noreply.github.com> --- protocol/fee-splitter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index 5c89d9c9..ce8b690f 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -10,7 +10,7 @@ ## Purpose -Enabling both the Superchain and OP Stack chain operators to manage their revenue in an easy to configure and transparent way will help to enable sustainable onchain businesses. The goals of this design doc are to enshrine fee split contracts as predeploys so that any existing or future OP Stack chain can easily benefit from battle tested fee split logic. +Enabling both Superchain and OP Stack chain operators to manage their revenue in a simple, transparent, and easily configurable way will support the development of sustainable onchain businesses. This design aims to enshrine fee split contracts as predeploys, ensuring that any existing or future OP Stack chain can benefit from battle-tested fee-split logic. ## Summary From 2af5ee17a541ada7a71e78f9c9601d5339edf094 Mon Sep 17 00:00:00 2001 From: Joxes <91908708+Joxess@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:19:25 -0300 Subject: [PATCH 4/9] docs: update flows Signed-off-by: Joxes <91908708+Joxess@users.noreply.github.com> --- protocol/fee-splitter.md | 71 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index ce8b690f..c38cf835 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -28,17 +28,74 @@ Fixed fee shares are too rigid: chains want custom splits, L1/L2 destinations, a ## Proposed Solution -![Diagram.png](https://img.notionusercontent.com/s3/prod-files-secure%2F7683bccd-1174-4689-a817-b27fd9d7ef00%2Fc6217459-59b2-4c32-bf56-7a028ed3bdcf%2FScreenshot_2025-08-25_at_13.47.02.png/size/w=1420?exp=1756395886&sig=fObiioU1Qwbf4ySWEiN7Mzyf5Fr86Yimtr3DJgKZZE0&id=25a9a4c0-92c7-80c2-a19a-da6dc330687b&table=block) - The `FeeSplitter` will be a predeploy with a modular config. The `SharesCalculator` and each `Recipient` are external entities that integrate into the system. High‑level flow: -1. Somebody (permissionless) calls `FeeSplitter.disburseFees()`. -2. Checks if the disbursement interval has passed. -3. Checks vault configs, pulls funds from vaults (if they reached the min withdrawal amount threshold), and computes per‑vault collected fees for granularity. -4. It calls the chain‑configured `SharesCalculator` with the revenue per vault as input to compute disbursements. -5. It receives the return data from `SharesCalculator`, validates outputs, stores the metadata on storage to make it externally accessible, and then transfers the amount to each recipient. +1. Anyone can call `FeeSplitter.disburseFees()`. The `FeeSpliter` checks the disbursement interval has elapsed. +2. For each `FeeVault`, the `FeeSplitter`: + - Verifies vaults configs. + - If valid, it calls `withdraw` from vaults (if they reached the min withdrawal amount threshold), pulling the funds. + - Computes a per‑vault collected fees for granularity. +3. The `FeeSplitter` calls the chain‑configured `SharesCalculator` with: + - The revenue per vault as input to compute disbursements. + - Receives data from `SharedCalculator` (amounts and outputs). +4. Finally, the `FeeSplitter` transfers the respective amount to each recipient and emit `FeesDisbursed`. + +```mermaid +graph LR + +User((User)) + +%% -------- Vaults row -------- +subgraph Vaults_L2["Fee Vaults"] + direction TB + BaseFeeVault[BFVault] + L1FeeVault[L1FVault] + SequencerFeeVault[SFVault] + OperatorFeeVault[OFVault] +end + +%% -------- Fee Splitter zone -------- +subgraph SplitterZone["Fee Splitter"] + direction LR + FeeSplitterNode[[FeeSplitter]] + SharesCalculator[SharesCalculator] + L2Recipient[EOA Recipient] + L2SC[Contracts] + L1Withdrawer[L1 Withdrawer] + L2ToL1MessagePasser[L2ToL1MP] +end + +%% -------- L1 side -------- +subgraph L1Zone["L1 Contracts"] + direction TB + Prover((User)) + OptimismPortal[OptimismPortal] + L1Recipient[L1 Recipient] +end + +%% -------- Flows -------- +User -->|"(1) disburseFees"| FeeSplitterNode + +BaseFeeVault -->|"(2) withdraw"| FeeSplitterNode +L1FeeVault -->|"(2) withdraw"| FeeSplitterNode +SequencerFeeVault -->|"(2) withdraw"| FeeSplitterNode +OperatorFeeVault -->|"(2) withdraw"| FeeSplitterNode + +FeeSplitterNode -->|"(3) getSharesRecipients"| SharesCalculator +SharesCalculator -.-> FeeSplitterNode + +FeeSplitterNode -->|"(4) send"| L2Recipient +FeeSplitterNode -->|"(4) send"| L2SC +FeeSplitterNode -->|"(4) send"| L1Withdrawer + +L1Withdrawer -->|"initiateWithdrawal"| L2ToL1MessagePasser +L2ToL1MessagePasser -.-> OptimismPortal +Prover -->|"prove and finalize"| OptimismPortal +OptimismPortal -->|"deliver"| L1Recipient + +``` **Invariants:** From ecad6c1a8c2f91b7575c3f25895a953c75b51121 Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Tue, 2 Sep 2025 10:43:01 -0300 Subject: [PATCH 5/9] Add overview diagram Signed-off-by: AgusDuha <81362284+agusduha@users.noreply.github.com> --- protocol/fee-splitter.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index c38cf835..ab7e5a9c 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -28,6 +28,8 @@ Fixed fee shares are too rigid: chains want custom splits, L1/L2 destinations, a ## Proposed Solution +rev-overview + The `FeeSplitter` will be a predeploy with a modular config. The `SharesCalculator` and each `Recipient` are external entities that integrate into the system. High‑level flow: From b542d297bd5d91722de6864d737faf3db7f2ebdf Mon Sep 17 00:00:00 2001 From: Disco <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 26 Sep 2025 18:45:16 -0300 Subject: [PATCH 6/9] feat: add new diagram (#28) --- protocol/fee-splitter.md | 60 ++-------------------------------------- 1 file changed, 3 insertions(+), 57 deletions(-) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index ab7e5a9c..a43eaea7 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -28,7 +28,8 @@ Fixed fee shares are too rigid: chains want custom splits, L1/L2 destinations, a ## Proposed Solution -rev-overview +image + The `FeeSplitter` will be a predeploy with a modular config. The `SharesCalculator` and each `Recipient` are external entities that integrate into the system. @@ -42,62 +43,7 @@ High‑level flow: 3. The `FeeSplitter` calls the chain‑configured `SharesCalculator` with: - The revenue per vault as input to compute disbursements. - Receives data from `SharedCalculator` (amounts and outputs). -4. Finally, the `FeeSplitter` transfers the respective amount to each recipient and emit `FeesDisbursed`. - -```mermaid -graph LR - -User((User)) - -%% -------- Vaults row -------- -subgraph Vaults_L2["Fee Vaults"] - direction TB - BaseFeeVault[BFVault] - L1FeeVault[L1FVault] - SequencerFeeVault[SFVault] - OperatorFeeVault[OFVault] -end - -%% -------- Fee Splitter zone -------- -subgraph SplitterZone["Fee Splitter"] - direction LR - FeeSplitterNode[[FeeSplitter]] - SharesCalculator[SharesCalculator] - L2Recipient[EOA Recipient] - L2SC[Contracts] - L1Withdrawer[L1 Withdrawer] - L2ToL1MessagePasser[L2ToL1MP] -end - -%% -------- L1 side -------- -subgraph L1Zone["L1 Contracts"] - direction TB - Prover((User)) - OptimismPortal[OptimismPortal] - L1Recipient[L1 Recipient] -end - -%% -------- Flows -------- -User -->|"(1) disburseFees"| FeeSplitterNode - -BaseFeeVault -->|"(2) withdraw"| FeeSplitterNode -L1FeeVault -->|"(2) withdraw"| FeeSplitterNode -SequencerFeeVault -->|"(2) withdraw"| FeeSplitterNode -OperatorFeeVault -->|"(2) withdraw"| FeeSplitterNode - -FeeSplitterNode -->|"(3) getSharesRecipients"| SharesCalculator -SharesCalculator -.-> FeeSplitterNode - -FeeSplitterNode -->|"(4) send"| L2Recipient -FeeSplitterNode -->|"(4) send"| L2SC -FeeSplitterNode -->|"(4) send"| L1Withdrawer - -L1Withdrawer -->|"initiateWithdrawal"| L2ToL1MessagePasser -L2ToL1MessagePasser -.-> OptimismPortal -Prover -->|"prove and finalize"| OptimismPortal -OptimismPortal -->|"deliver"| L1Recipient - -``` +4. Finally, the `FeeSplitter` transfers the respective amount to each recipient and emit `FeesDisbursed`. One possible subsequent flow is for the `L1Withdrawer` to withdraw to the `FeesDepositor`, which automatically triggers a deposit on the `OptimismPortal` on L1. **Invariants:** From fd3f500967d71562c3593ae8cd3fd6a63867de0e Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:01:17 -0300 Subject: [PATCH 7/9] fix: diagram --- protocol/fee-splitter.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index a43eaea7..ce9d1128 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -28,8 +28,7 @@ Fixed fee shares are too rigid: chains want custom splits, L1/L2 destinations, a ## Proposed Solution -image - +image The `FeeSplitter` will be a predeploy with a modular config. The `SharesCalculator` and each `Recipient` are external entities that integrate into the system. From 4dce4ecfa4b3bc89107dc27eb3d4fbc85de8016f Mon Sep 17 00:00:00 2001 From: Joxes <91908708+Joxess@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:40:42 -0300 Subject: [PATCH 8/9] docs: add threat model outputs Signed-off-by: Joxes <91908708+Joxess@users.noreply.github.com> --- protocol/fee-splitter.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index ce9d1128..312fce68 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -42,7 +42,7 @@ High‑level flow: 3. The `FeeSplitter` calls the chain‑configured `SharesCalculator` with: - The revenue per vault as input to compute disbursements. - Receives data from `SharedCalculator` (amounts and outputs). -4. Finally, the `FeeSplitter` transfers the respective amount to each recipient and emit `FeesDisbursed`. One possible subsequent flow is for the `L1Withdrawer` to withdraw to the `FeesDepositor`, which automatically triggers a deposit on the `OptimismPortal` on L1. +4. Finally, the `FeeSplitter` transfers the respective amount to each recipient and emit `FeesDisbursed`. One possible subsequent flow is for the `L1Withdrawer` to withdraw to the `FeesDepositor`, which automatically triggers a deposit on the `OptimismPortal` on L1. This subsequent flow is expected when an OP Stack Chain shares its revenue with the Superchain ecosystem. **Invariants:** @@ -110,7 +110,31 @@ To calculate the share to send to the `revenueShareRecpient`, it will get the `g ## Risks & Uncertainties -- If a calculator misbehaves (sum mismatch or downstream recipients revert), disbursements will revert. We’ll validate outputs and keep calculator swaps gated by `ProxyAdmin.owner()`. +### Chain’s collected fees are lost + +Bugs or misconfigurations in `FeeSplitter`, `SharesCalculator`, any `FeeVault`, and recipients (including withdrawal flows) could result in funds being transferred to the wrong party or to an address that can’t recover them. The most likely vectors include: + +- Wrong `FeeVault` migration process +- Faulty recipient list in a `SharesCalculator` +- Faulty recipient in the `L1Withdrawer` +- Faulty recipient in the `FeeDepositor` +- Critical bugs in any piece of the system + +These risks are mitigated with proper testing and by reducing the access-control surfaces to the `ProxyAdmin.owner()`. Chain operators must be aware of the proper setup of each recipient and if other kind of `SharesCalculator` is used. Superchain Ops scripts should be included in the audit scope to mitigate the risk of the migration process. + +### Collected Fees are Stuck + +Disbursements can fail (and keep failing) if the logic reverts at any step. Cases where this could happen include + +- Issues in the `FeeVault` migration process. +- `FeeSplitter` misconfiguration, such as an excessively large disbursement interval (even if permitted by an unreasonable maximum). +- `SharesCalculator`-induced reverts due to bugs or issues in the set of recipients. + +These risks are mitigated through thorough testing and access controls to change thresholds or recipients safely. The `SuperchainRevSharesCalculator` as well as any other `SharesCalculator`, requires an audit and proper documentation to avoid DoS and revert scenarios. + +### Recipient Receiving a Wrong Amount + +An incorrect distribution may occur if there is a bug in a `SharesCalculator`. A standard smart-contract audit must mitigate this possibility. ## Appendix From a1f555356a723f2d58ea0bf16fbc164b1a285760 Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Fri, 10 Oct 2025 15:06:45 -0300 Subject: [PATCH 9/9] fix: update diagram Signed-off-by: AgusDuha <81362284+agusduha@users.noreply.github.com> --- protocol/fee-splitter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/fee-splitter.md b/protocol/fee-splitter.md index 312fce68..b83cd553 100644 --- a/protocol/fee-splitter.md +++ b/protocol/fee-splitter.md @@ -28,7 +28,7 @@ Fixed fee shares are too rigid: chains want custom splits, L1/L2 destinations, a ## Proposed Solution -image +splitter-diagram The `FeeSplitter` will be a predeploy with a modular config. The `SharesCalculator` and each `Recipient` are external entities that integrate into the system.