diff --git a/.gas-snapshot b/.gas-snapshot index b3a3bea..152f635 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,8 +1,9 @@ -GasbackTest:testConvertGasback() (gas: 50450) -GasbackTest:testConvertGasback(uint256,uint256) (runs: 256, μ: 370473, ~: 135584) -GasbackTest:testConvertGasbackBaseFeeVault() (gas: 24310) -GasbackTest:testConvertGasbackMaxBaseFee() (gas: 21878) -GasbackTest:testConvertGasbackMinVaultBalance() (gas: 24154) -GasbackTest:test__codesize() (gas: 8178) +GasbackTest:testConvertGasback() (gas: 73039) +GasbackTest:testConvertGasback(uint256,uint256) (runs: 257, μ: 423506, ~: 308109) +GasbackTest:testConvertGasbackBaseFeeVault() (gas: 27070) +GasbackTest:testConvertGasbackMaxBaseFee() (gas: 44525) +GasbackTest:testConvertGasbackMinVaultBalance() (gas: 26953) +GasbackTest:testConvertGasbackWithAccruedToAccruedRecipient() (gas: 69305) +GasbackTest:test__codesize() (gas: 9846) SoladyTest:test__codesize() (gas: 4099) TestPlus:test__codesize() (gas: 393) \ No newline at end of file diff --git a/src/Gasback.sol b/src/Gasback.sol index 978507e..b7f3270 100644 --- a/src/Gasback.sol +++ b/src/Gasback.sol @@ -34,6 +34,8 @@ contract Gasback { uint256 minVaultBalance; // The amount of ETH accrued by taking a cut from the gas burned. uint256 accrued; + // The recipient of the accrued ETH. + address accruedRecipient; // A mapping of addresses authorized to withdraw the accrued ETH. mapping(address => bool) accuralWithdrawers; } @@ -58,6 +60,7 @@ contract Gasback { $.gasbackMaxBaseFee = type(uint256).max; $.baseFeeVault = 0x4200000000000000000000000000000000000019; $.minVaultBalance = 0.42 ether; + $.accruedRecipient = 0x4200000000000000000000000000000000000019; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ @@ -120,6 +123,27 @@ contract Gasback { return true; } + /// @dev Withdraws from the accrued amount to the accrued recipient. + function withdrawAccruedToAccruedRecipient(uint256 amount) public virtual returns (bool) { + // Checked math prevents underflow. + _getGasbackStorage().accrued -= amount; + + address accruedRecipient = _getGasbackStorage().accruedRecipient; + /// @solidity memory-safe-assembly + assembly { + if iszero(call(gas(), accruedRecipient, amount, 0x00, 0x00, 0x00, 0x00)) { + revert(0x00, 0x00) + } + } + return true; + } + + /// @dev Sets the accrued recipient. + function setAccruedRecipient(address value) public onlySystemOrThis returns (bool) { + _getGasbackStorage().accruedRecipient = value; + return true; + } + /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* ADMIN FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ diff --git a/test/Gasback.t.sol b/test/Gasback.t.sol index 03463f1..1710cc2 100644 --- a/test/Gasback.t.sol +++ b/test/Gasback.t.sol @@ -75,4 +75,30 @@ contract GasbackTest is SoladyTest { assertTrue(success); assertEq(pranker.balance, 0); } + + function testConvertGasbackWithAccruedToAccruedRecipient() public { + address system = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE; + vm.prank(system); + gasback.setAccruedRecipient(address(42)); + + uint256 baseFee = 1 ether; + uint256 gasToBurn = 333; + + address pranker = address(111); + vm.fee(baseFee); + vm.deal(pranker, 1000 ether); + + vm.prank(pranker); + (bool success,) = address(gasback).call(abi.encode(gasToBurn)); + assertTrue(success); + + uint256 accrued = gasback.accrued(); + + assertNotEq(accrued, 0); + + vm.prank(pranker); + gasback.withdrawAccruedToAccruedRecipient(accrued); + + assertEq(address(42).balance, accrued); + } }