From 0c6b0d372905913c094fcccc29348450439d8551 Mon Sep 17 00:00:00 2001 From: j6i <43463199+j6i@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:36:57 -0700 Subject: [PATCH 1/5] install --- foundry.lock | 8 ++++++++ remappings.txt | 1 + 2 files changed, 9 insertions(+) create mode 100644 foundry.lock create mode 100644 remappings.txt diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 0000000..2a8215d --- /dev/null +++ b/foundry.lock @@ -0,0 +1,8 @@ +{ + "lib/forge-std": { + "rev": "77041d2ce690e692d6e03cc812b57d1ddaa4d505" + }, + "lib/solady": { + "rev": "4363564a984779b7eec3bff00ab1de3a9db4e2d5" + } +} \ No newline at end of file diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 0000000..159c5f8 --- /dev/null +++ b/remappings.txt @@ -0,0 +1 @@ +@openzeppelin-contracts-4.9.5/=dependencies/@openzeppelin-contracts-4.9.5/ From fc1148a0a58e011978d637885c3483776b071183 Mon Sep 17 00:00:00 2001 From: j6i <43463199+j6i@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:17:43 -0700 Subject: [PATCH 2/5] first updates --- src/Gasback.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Gasback.sol b/src/Gasback.sol index 5b15a23..7477f86 100644 --- a/src/Gasback.sol +++ b/src/Gasback.sol @@ -4,6 +4,9 @@ pragma solidity ^0.8.7; /// @dev A contract that converts a portion of the gas burned into ETH. /// This contract holds ETH deposited by the sequencer, which will be /// redistributed to callers. +/// @dev Configuration notes: +/// - The baseFeeVault's WITHDRAWAL_NETWORK should be configured to withdraw to the network this contract is deployed on. +/// - The baseFeeVault's MIN_WITHDRAWAL_AMOUNT should be set to a reasonable value below ethToGive in order for withdrawals to be successful. contract Gasback { /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONSTANTS */ @@ -46,6 +49,7 @@ contract Gasback { /* CONSTRUCTOR */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ + /// @dev When using this contract with EIP-7702, setters must be called to configure the constructor values. constructor() payable { GasbackStorage storage $ = _getGasbackStorage(); $.gasbackRatioNumerator = 0.6 ether; @@ -136,6 +140,7 @@ contract Gasback { /// @dev For the gasback logic. fallback() external payable { + require(tx.gasprice != 0, "Gasback: gasprice is 0"); // Prevents L1 -> L2 deposits from being used. uint256 gasToBurn; /// @solidity memory-safe-assembly @@ -159,7 +164,7 @@ contract Gasback { if (ethToGive > selfBalance && block.basefee <= $.gasbackMaxBaseFee) { /// @solidity memory-safe-assembly assembly { - mstore(0x00, 0xc70746b1) // `triggerBaseFeeVaultWithdraw(uint256)`. + mstore(0x00, 0xc70746b1) // `triggerBaseFeeVaultWithdraw(uint256)`. we don't check success here because it will revert if the base fee vault is out of ETH. mstore(0x20, ethToGive) pop(call(gas(), address(), 0, 0x1c, 0x24, 0x00, 0x00)) } From 6c774ec9f1ac299f5cb642cffedbd7141e833685 Mon Sep 17 00:00:00 2001 From: j6i <43463199+j6i@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:37:14 -0700 Subject: [PATCH 3/5] add comment about risks --- src/FeeVaultSplitter.sol | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/FeeVaultSplitter.sol b/src/FeeVaultSplitter.sol index aac96eb..8133cee 100644 --- a/src/FeeVaultSplitter.sol +++ b/src/FeeVaultSplitter.sol @@ -40,12 +40,14 @@ contract FeeVaultSplitter is PaymentSplitter, ReentrancyGuard { * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback * functions]. * - * SECURITY / DoS NOTE (push-payment model): this function attempts to release to every payee in - * `externalPayees` in a single call. Its gas cost therefore scales with the payee count, and a payee whose - * `receive`/fallback consumes a large amount of gas (rather than cheaply reverting, which is caught and skipped) - * can push this call out of gas and make it revert. Because the OP base fee vault's `withdraw()` sends fees to - * this contract (triggering `receive`), such a revert would block that withdrawal and strand base fees in the - * vault until resolved. To bound this risk: keep the payee set small and trusted (it is fixed at deployment). + * SECURITY / DoS NOTE (push-payment model): this function attempts to release to every payee in `externalPayees` + * in a single call. Its gas cost therefore scales with the payee count, and a payee whose `receive`/fallback + * consumes a large amount of gas (rather than cheaply reverting, which is caught and skipped) can push this call + * out of gas and make it revert. Because the OP base fee vault's `withdraw()` sends fees to this contract + * (triggering `receive`), such a revert would block that withdrawal and strand base fees in the vault until resolved. + * Additionally, a malicious payee ordered after the Gasback contract in `payees_` can re-enter the Gasback contract + * during distribution and use up the ETH just released to it, leaving no ETH for the original caller so their call is skipped. + * To bound this risk: keep the payee set small and trusted (it is fixed at deployment). * If a deposit's auto-distribution is ever blocked, funds are not lost — anyone can call {distribute} with a * bounded `[start, end)` slice to release payees in chunks and recover. */ From 254897e1468f298dbf3cf99fbff52d7f912816e6 Mon Sep 17 00:00:00 2001 From: j6i <43463199+j6i@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:43:22 -0700 Subject: [PATCH 4/5] add gas price to tests --- test/Gasback.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Gasback.t.sol b/test/Gasback.t.sol index e84c067..861ed56 100644 --- a/test/Gasback.t.sol +++ b/test/Gasback.t.sol @@ -32,6 +32,7 @@ contract GasbackTest is SoladyTest { Gasback public gasback; function setUp() public { + vm.txGasPrice(1); gasback = new Gasback(); vm.deal(address(gasback), 2 ** 160); } From 05ba27b8eb25c53b9587d16fa623694a6bda5008 Mon Sep 17 00:00:00 2001 From: j6i <43463199+j6i@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:45:41 -0700 Subject: [PATCH 5/5] snapshot, fmt --- .gas-snapshot | 22 +++++++++++----------- test/FeeVaultSplitter.t.sol | 1 - test/utils/TestPlus.sol | 6 +++++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 017faa5..baf7667 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,5 +1,5 @@ -FeeVaultSplitterTest:testFuzz_balances_after_multiple_payments(uint8,uint256[9]) (runs: 256, μ: 4744057, ~: 3749281) -FeeVaultSplitterTest:testFuzz_balances_after_payment(uint8,uint256) (runs: 256, μ: 2848294, ~: 1764000) +FeeVaultSplitterTest:testFuzz_balances_after_multiple_payments(uint8,uint256[9]) (runs: 257, μ: 4633841, ~: 3278232) +FeeVaultSplitterTest:testFuzz_balances_after_payment(uint8,uint256) (runs: 257, μ: 2747347, ~: 1764012) FeeVaultSplitterTest:test__codesize() (gas: 30307) FeeVaultSplitterTest:test_balances_after_payment() (gas: 262015) FeeVaultSplitterTest:test_distribute_clamps_end_to_payees_length() (gas: 247482) @@ -24,20 +24,20 @@ FeeVaultSplitterTest:test_revert_release_account_has_no_shares() (gas: 11672) FeeVaultSplitterTest:test_revert_release_account_not_due_payment() (gas: 20633) FeeVaultSplitterTest:test_revert_release_failed_to_send_value() (gas: 1047207) FeeVaultSplitterTest:test_revert_release_insufficient_balance() (gas: 36608) -GasbackTest:testConvertGasback() (gas: 52487) -GasbackTest:testConvertGasback(uint256,uint256) (runs: 256, μ: 416918, ~: 279220) -GasbackTest:testConvertGasbackCodelessBaseFeeVaultPassesThrough() (gas: 23689) -GasbackTest:testConvertGasbackMaxBaseFee() (gas: 22353) -GasbackTest:testConvertGasbackPullsFromDirectRecipientBaseFeeVault() (gas: 1220102) -GasbackTest:testConvertGasbackPullsFromSplitterRecipientBaseFeeVault() (gas: 2271856) -GasbackTest:testConvertGasbackRevertingBaseFeeVaultPassesThrough() (gas: 180145) -GasbackTest:testConvertGasbackRevertsInnerWithdrawWhenSplitterShareInsufficient() (gas: 1241543) +GasbackTest:testConvertGasback() (gas: 52505) +GasbackTest:testConvertGasback(uint256,uint256) (runs: 257, μ: 414672, ~: 315288) +GasbackTest:testConvertGasbackCodelessBaseFeeVaultPassesThrough() (gas: 23707) +GasbackTest:testConvertGasbackMaxBaseFee() (gas: 22371) +GasbackTest:testConvertGasbackPullsFromDirectRecipientBaseFeeVault() (gas: 1220120) +GasbackTest:testConvertGasbackPullsFromSplitterRecipientBaseFeeVault() (gas: 2271874) +GasbackTest:testConvertGasbackRevertingBaseFeeVaultPassesThrough() (gas: 180163) +GasbackTest:testConvertGasbackRevertsInnerWithdrawWhenSplitterShareInsufficient() (gas: 1241561) GasbackTest:testSetGasbackRatioNumeratorAcceptsScriptValue() (gas: 15541) GasbackTest:testSetGasbackRatioNumeratorRevertsWhenValueAboveDenominator() (gas: 9672) GasbackTest:testTriggerBaseFeeVaultWithdrawRevertsWhenCallerIsNotSelf() (gas: 8751) GasbackTest:testWithdrawRevertsWhenCallerUnauthorized() (gas: 9543) GasbackTest:testWithdrawTransfersEthWhenCallerSelf() (gas: 45507) GasbackTest:testWithdrawTransfersEthWhenCallerSystem() (gas: 45643) -GasbackTest:test__codesize() (gas: 20055) +GasbackTest:test__codesize() (gas: 20277) SoladyTest:test__codesize() (gas: 4099) TestPlus:test__codesize() (gas: 393) \ No newline at end of file diff --git a/test/FeeVaultSplitter.t.sol b/test/FeeVaultSplitter.t.sol index 31fb644..540f8c3 100644 --- a/test/FeeVaultSplitter.t.sol +++ b/test/FeeVaultSplitter.t.sol @@ -582,4 +582,3 @@ contract FeeVaultSplitterTest is SoladyTest { assertEq(address(splitter).balance, 0); } } - diff --git a/test/utils/TestPlus.sol b/test/utils/TestPlus.sol index ae8d095..ea13e1d 100644 --- a/test/utils/TestPlus.sol +++ b/test/utils/TestPlus.sol @@ -553,7 +553,11 @@ contract TestPlus is Brutalizer { /// @dev Truncate the bytes to `n` bytes. /// Returns the result for function chaining. - function _truncateBytes(bytes memory b, uint256 n) internal pure returns (bytes memory result) { + function _truncateBytes(bytes memory b, uint256 n) + internal + pure + returns (bytes memory result) + { /// @solidity memory-safe-assembly assembly { if gt(mload(b), n) { mstore(b, n) }