From 473abf114cb49366890f0050180e0baa3507f2d1 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Sun, 24 Sep 2023 17:09:45 +0100 Subject: [PATCH 01/12] chore: remove token from OWRFactory --- src/owr/OptimisticWithdrawalRecipientFactory.sol | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/owr/OptimisticWithdrawalRecipientFactory.sol b/src/owr/OptimisticWithdrawalRecipientFactory.sol index 7cf9c2d..04b31a7 100644 --- a/src/owr/OptimisticWithdrawalRecipientFactory.sol +++ b/src/owr/OptimisticWithdrawalRecipientFactory.sol @@ -14,9 +14,6 @@ contract OptimisticWithdrawalRecipientFactory { /// errors /// ----------------------------------------------------------------------- - /// Invalid token - error Invalid_Token(); - /// Invalid number of recipients, must be 2 error Invalid__Recipients(); @@ -39,7 +36,6 @@ contract OptimisticWithdrawalRecipientFactory { /// Emitted after a new OptimisticWithdrawalRecipient module is deployed /// @param owr Address of newly created OptimisticWithdrawalRecipient clone - /// @param token Address of ERC20 to distribute (0x0 used for ETH) /// @param recoveryAddress Address to recover non-OWR tokens to /// @param principalRecipient Address to distribute principal payment to /// @param rewardRecipient Address to distribute reward payment to @@ -47,7 +43,6 @@ contract OptimisticWithdrawalRecipientFactory { /// (reward recipient has no threshold & receives all residual flows) event CreateOWRecipient( address indexed owr, - address token, address recoveryAddress, address principalRecipient, address rewardRecipient, @@ -80,7 +75,6 @@ contract OptimisticWithdrawalRecipientFactory { /// ----------------------------------------------------------------------- /// Create a new OptimisticWithdrawalRecipient clone - /// @param token Address of ERC20 to distribute (0x0 used for ETH) /// @param recoveryAddress Address to recover non-OWR tokens to /// If this address is 0x0, recovery of unrelated tokens can be completed by /// either the principal or reward recipients. If this address is set, only @@ -94,7 +88,6 @@ contract OptimisticWithdrawalRecipientFactory { /// it cannot be greater than uint96 /// @return owr Address of new OptimisticWithdrawalRecipient clone function createOWRecipient( - address token, address recoveryAddress, address principalRecipient, address rewardRecipient, @@ -115,11 +108,11 @@ contract OptimisticWithdrawalRecipientFactory { // would not exceed contract size limits // important to not reorder - bytes memory data = abi.encodePacked(token, recoveryAddress, principalData, rewardData); + bytes memory data = abi.encodePacked(recoveryAddress, principalData, rewardData); owr = OptimisticWithdrawalRecipient(address(owrImpl).clone(data)); emit CreateOWRecipient( - address(owr), token, recoveryAddress, principalRecipient, rewardRecipient, amountOfPrincipalStake + address(owr), recoveryAddress, principalRecipient, rewardRecipient, amountOfPrincipalStake ); } } From 8093970d855a712ef7a83c40d257b1afff9b0e7f Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Sun, 24 Sep 2023 17:21:57 +0100 Subject: [PATCH 02/12] chore: remove token from OWR implementation --- src/owr/OptimisticWithdrawalRecipient.sol | 62 ++++++------------- .../OptimisticWithdrawalRecipientFactory.sol | 2 +- 2 files changed, 19 insertions(+), 45 deletions(-) diff --git a/src/owr/OptimisticWithdrawalRecipient.sol b/src/owr/OptimisticWithdrawalRecipient.sol index 7fda447..02ffa69 100644 --- a/src/owr/OptimisticWithdrawalRecipient.sol +++ b/src/owr/OptimisticWithdrawalRecipient.sol @@ -9,10 +9,8 @@ import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; /// @author Obol /// @notice A maximally-composable contract that distributes payments /// based on threshold to it's recipients -/// @dev Only one token can be distributed for a given deployment. There is a -/// recovery method for non-target tokens sent by accident. -/// Target ERC20s with very large decimals may overflow & cause issues. -/// This contract uses token = address(0) to refer to ETH. +/// @dev Only ETH can be distributed for a given deployment. There is a +/// recovery method for tokens sent by accident. contract OptimisticWithdrawalRecipient is Clone { /// ----------------------------------------------------------------------- /// libraries @@ -24,9 +22,6 @@ contract OptimisticWithdrawalRecipient is Clone { /// errors /// ----------------------------------------------------------------------- - /// Invalid token recovery; cannot recover the OWRecipient token - error InvalidTokenRecovery_OWRToken(); - /// Invalid token recovery recipient error InvalidTokenRecovery_InvalidRecipient(); @@ -49,9 +44,9 @@ contract OptimisticWithdrawalRecipient is Clone { /// pulling event DistributeFunds(uint256 principalPayout, uint256 rewardPayout, uint256 pullFlowFlag); - /// Emitted after non-OWRecipient tokens are recovered to a recipient + /// Emitted after tokens are recovered to a recipient /// @param recoveryAddressToken Recovered token (cannot be - /// OptimisticWithdrawalRecipient token) + /// ETH) /// @param recipient Address receiving recovered token /// @param amount Amount of recovered token event RecoverNonOWRecipientFunds(address recoveryAddressToken, address recipient, uint256 amount); @@ -86,22 +81,14 @@ contract OptimisticWithdrawalRecipient is Clone { /// storage - cwia offsets /// ----------------------------------------------------------------------- - // token (address, 20 bytes), recoveryAddress (address, 20 bytes), + // recoveryAddress (address, 20 bytes), // tranches (uint256[], numTranches * 32 bytes) // 0; first item - uint256 internal constant TOKEN_OFFSET = 0; - // 20 = token_offset (0) + token_size (address, 20 bytes) - uint256 internal constant RECOVERY_ADDRESS_OFFSET = 20; - // 40 = recoveryAddress_offset (20) + recoveryAddress_size (address, 20 + uint256 internal constant RECOVERY_ADDRESS_OFFSET = 0; + // 20 = recoveryAddress_offset (0) + recoveryAddress_size (address, 20 // bytes) - uint256 internal constant TRANCHES_OFFSET = 40; - - /// Address of ERC20 to distribute (0x0 used for ETH) - /// @dev equivalent to address public immutable token; - function token() public pure returns (address) { - return _getArgAddress(TOKEN_OFFSET); - } + uint256 internal constant TRANCHES_OFFSET = 20; /// Address to recover non-OWR tokens to /// @dev equivalent to address public immutable recoveryAddress; @@ -179,9 +166,6 @@ contract OptimisticWithdrawalRecipient is Clone { function recoverFunds(address nonOWRToken, address recipient) external payable { /// checks - // revert if caller tries to recover OWRecipient token - if (nonOWRToken == token()) revert InvalidTokenRecovery_OWRToken(); - // if recoveryAddress is set, recipient must match it // else, recipient must be one of the OWR recipients @@ -201,30 +185,22 @@ contract OptimisticWithdrawalRecipient is Clone { /// interactions // recover non-target token - uint256 amount; - if (nonOWRToken == ETH_ADDRESS) { - amount = address(this).balance; - recipient.safeTransferETH(amount); - } else { - amount = ERC20(nonOWRToken).balanceOf(address(this)); - nonOWRToken.safeTransfer(recipient, amount); - } - + uint256 amount = ERC20(nonOWRToken).balanceOf(address(this)); + nonOWRToken.safeTransfer(recipient, amount); + emit RecoverNonOWRecipientFunds(nonOWRToken, recipient, amount); } /// Withdraw token balance for account `account` /// @param account Address to withdraw on behalf of function withdraw(address account) external { - address _token = token(); uint256 tokenAmount = pullBalances[account]; unchecked { // shouldn't underflow; fundsPendingWithdrawal = sum(pullBalances) fundsPendingWithdrawal -= uint128(tokenAmount); } pullBalances[account] = 0; - if (_token == ETH_ADDRESS) account.safeTransferETH(tokenAmount); - else _token.safeTransfer(account, tokenAmount); + account.safeTransferETH(tokenAmount); emit Withdrawal(account, tokenAmount); } @@ -269,7 +245,7 @@ contract OptimisticWithdrawalRecipient is Clone { // load storage into memory // fetch the token we want to distribute - address _token = token(); + // address _token = token(); // the amount of funds distributed so far uint256 _startingDistributedFunds = uint256(distributedFunds); uint256 _endingDistributedFunds; @@ -283,7 +259,7 @@ contract OptimisticWithdrawalRecipient is Clone { - _memoryFundsPendingWithdrawal // recognizes 0x0 as ETH // shouldn't need to worry about re-entrancy from ERC20 view fn - + (_token == ETH_ADDRESS ? address(this).balance : ERC20(_token).balanceOf(address(this))); + + address(this).balance; _fundsToBeDistributed = _endingDistributedFunds - _startingDistributedFunds; } @@ -331,9 +307,9 @@ contract OptimisticWithdrawalRecipient is Clone { // when later external calls fail (bc balance is emptied early) // pay out principal - _payout(_token, principalRecipient, _principalPayout, pullFlowFlag); + _payout(principalRecipient, _principalPayout, pullFlowFlag); // pay out reward - _payout(_token, rewardRecipient, _rewardPayout, pullFlowFlag); + _payout(rewardRecipient, _rewardPayout, pullFlowFlag); if (pullFlowFlag == PULL) { if (_principalPayout > 0 || _rewardPayout > 0) { @@ -345,15 +321,13 @@ contract OptimisticWithdrawalRecipient is Clone { emit DistributeFunds(_principalPayout, _rewardPayout, pullFlowFlag); } - function _payout(address payoutToken, address recipient, uint256 payoutAmount, uint256 pullFlowFlag) internal { + function _payout(address recipient, uint256 payoutAmount, uint256 pullFlowFlag) internal { if (payoutAmount > 0) { if (pullFlowFlag == PULL) { // Write to Storage pullBalances[recipient] += payoutAmount; - } else if (payoutToken == ETH_ADDRESS) { - recipient.safeTransferETH(payoutAmount); } else { - payoutToken.safeTransfer(recipient, payoutAmount); + recipient.safeTransferETH(payoutAmount); } } } diff --git a/src/owr/OptimisticWithdrawalRecipientFactory.sol b/src/owr/OptimisticWithdrawalRecipientFactory.sol index 04b31a7..39bda87 100644 --- a/src/owr/OptimisticWithdrawalRecipientFactory.sol +++ b/src/owr/OptimisticWithdrawalRecipientFactory.sol @@ -75,7 +75,7 @@ contract OptimisticWithdrawalRecipientFactory { /// ----------------------------------------------------------------------- /// Create a new OptimisticWithdrawalRecipient clone - /// @param recoveryAddress Address to recover non-OWR tokens to + /// @param recoveryAddress Address to recover tokens to /// If this address is 0x0, recovery of unrelated tokens can be completed by /// either the principal or reward recipients. If this address is set, only /// this address can recover From c53a831f604a0e28777e90d93ec695593af2d383 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Sun, 24 Sep 2023 17:22:13 +0100 Subject: [PATCH 03/12] chore: apply forge fmt --- src/owr/OptimisticWithdrawalRecipient.sol | 2 +- src/owr/OptimisticWithdrawalRecipientFactory.sol | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/owr/OptimisticWithdrawalRecipient.sol b/src/owr/OptimisticWithdrawalRecipient.sol index 02ffa69..5e63f42 100644 --- a/src/owr/OptimisticWithdrawalRecipient.sol +++ b/src/owr/OptimisticWithdrawalRecipient.sol @@ -187,7 +187,7 @@ contract OptimisticWithdrawalRecipient is Clone { // recover non-target token uint256 amount = ERC20(nonOWRToken).balanceOf(address(this)); nonOWRToken.safeTransfer(recipient, amount); - + emit RecoverNonOWRecipientFunds(nonOWRToken, recipient, amount); } diff --git a/src/owr/OptimisticWithdrawalRecipientFactory.sol b/src/owr/OptimisticWithdrawalRecipientFactory.sol index 39bda87..a6331c3 100644 --- a/src/owr/OptimisticWithdrawalRecipientFactory.sol +++ b/src/owr/OptimisticWithdrawalRecipientFactory.sol @@ -42,11 +42,7 @@ contract OptimisticWithdrawalRecipientFactory { /// @param threshold Absolute payment threshold for OWR first recipient /// (reward recipient has no threshold & receives all residual flows) event CreateOWRecipient( - address indexed owr, - address recoveryAddress, - address principalRecipient, - address rewardRecipient, - uint256 threshold + address indexed owr, address recoveryAddress, address principalRecipient, address rewardRecipient, uint256 threshold ); /// ----------------------------------------------------------------------- @@ -111,8 +107,6 @@ contract OptimisticWithdrawalRecipientFactory { bytes memory data = abi.encodePacked(recoveryAddress, principalData, rewardData); owr = OptimisticWithdrawalRecipient(address(owrImpl).clone(data)); - emit CreateOWRecipient( - address(owr), recoveryAddress, principalRecipient, rewardRecipient, amountOfPrincipalStake - ); + emit CreateOWRecipient(address(owr), recoveryAddress, principalRecipient, rewardRecipient, amountOfPrincipalStake); } } From f64b72d597ac392b27dcb4d281843af5bf2aab70 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Sun, 24 Sep 2023 17:32:45 +0100 Subject: [PATCH 04/12] chore: remove distributedFunds variable from OWR - issue #80 --- src/owr/OptimisticWithdrawalRecipient.sol | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/owr/OptimisticWithdrawalRecipient.sol b/src/owr/OptimisticWithdrawalRecipient.sol index 5e63f42..b1fb6a5 100644 --- a/src/owr/OptimisticWithdrawalRecipient.sol +++ b/src/owr/OptimisticWithdrawalRecipient.sol @@ -111,7 +111,7 @@ contract OptimisticWithdrawalRecipient is Clone { /// Amount of distributed OWRecipient token /// @dev ERC20s with very large decimals may overflow & cause issues - uint128 public distributedFunds; + // uint128 public distributedFunds; /// Amount of active balance set aside for pulls /// @dev ERC20s with very large decimals may overflow & cause issues @@ -244,23 +244,12 @@ contract OptimisticWithdrawalRecipient is Clone { /// effects // load storage into memory - // fetch the token we want to distribute - // address _token = token(); - // the amount of funds distributed so far - uint256 _startingDistributedFunds = uint256(distributedFunds); - uint256 _endingDistributedFunds; + uint256 currentbalance = address(this).balance; uint256 _fundsToBeDistributed; uint256 _claimedPrincipalFunds = uint256(claimedPrincipalFunds); uint256 _memoryFundsPendingWithdrawal = uint256(fundsPendingWithdrawal); unchecked { - // shouldn't overflow - _endingDistributedFunds = _startingDistributedFunds - // fundsPendingWithdrawal is always <= _startingDistributedFunds - - _memoryFundsPendingWithdrawal - // recognizes 0x0 as ETH - // shouldn't need to worry about re-entrancy from ERC20 view fn - + address(this).balance; - _fundsToBeDistributed = _endingDistributedFunds - _startingDistributedFunds; + _fundsToBeDistributed = currentbalance - _memoryFundsPendingWithdrawal; } (address principalRecipient, address rewardRecipient, uint256 amountOfPrincipalStake) = getTranches(); @@ -292,9 +281,7 @@ contract OptimisticWithdrawalRecipient is Clone { } { - if (_endingDistributedFunds > type(uint128).max) revert InvalidDistribution_TooLarge(); // Write to storage - distributedFunds = uint128(_endingDistributedFunds); // the principal value claimedPrincipalFunds += _principalPayout; } From 0a8eceb381e16a16211dea4445cc92942c622649 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Sun, 24 Sep 2023 17:37:02 +0100 Subject: [PATCH 05/12] mend --- src/owr/OptimisticWithdrawalRecipient.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/owr/OptimisticWithdrawalRecipient.sol b/src/owr/OptimisticWithdrawalRecipient.sol index b1fb6a5..450c870 100644 --- a/src/owr/OptimisticWithdrawalRecipient.sol +++ b/src/owr/OptimisticWithdrawalRecipient.sol @@ -109,10 +109,6 @@ contract OptimisticWithdrawalRecipient is Clone { /// storage - mutables /// ----------------------------------------------------------------------- - /// Amount of distributed OWRecipient token - /// @dev ERC20s with very large decimals may overflow & cause issues - // uint128 public distributedFunds; - /// Amount of active balance set aside for pulls /// @dev ERC20s with very large decimals may overflow & cause issues uint128 public fundsPendingWithdrawal; From 490da9458d49d4442e5ca3e009301d3d0ad9f550 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Sun, 24 Sep 2023 17:49:26 +0100 Subject: [PATCH 06/12] mend --- src/owr/OptimisticWithdrawalRecipient.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/owr/OptimisticWithdrawalRecipient.sol b/src/owr/OptimisticWithdrawalRecipient.sol index 450c870..6d4a697 100644 --- a/src/owr/OptimisticWithdrawalRecipient.sol +++ b/src/owr/OptimisticWithdrawalRecipient.sol @@ -64,8 +64,6 @@ contract OptimisticWithdrawalRecipient is Clone { /// storage - constants /// ----------------------------------------------------------------------- - address internal constant ETH_ADDRESS = address(0); - uint256 internal constant PUSH = 0; uint256 internal constant PULL = 1; From 6d3dd94162b9bea469db0df580188a938c88a536 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Sun, 24 Sep 2023 23:43:04 +0100 Subject: [PATCH 07/12] test: fix OWR and OWRFactory test cases --- src/test/owr/OWRTestHelper.t.sol | 2 +- .../owr/OptimisticWithdrawalRecipient.t.sol | 370 ++---------------- ...OptimisticWithdrawalRecipientFactory.t.sol | 80 +--- 3 files changed, 42 insertions(+), 410 deletions(-) diff --git a/src/test/owr/OWRTestHelper.t.sol b/src/test/owr/OWRTestHelper.t.sol index 787cac4..1451b2a 100644 --- a/src/test/owr/OWRTestHelper.t.sol +++ b/src/test/owr/OWRTestHelper.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.17; contract OWRTestHelper { - address internal constant ETH_ADDRESS = address(0); + // address internal constant ETH_ADDRESS = address(0); uint256 internal constant MAX_TRANCHE_SIZE = 2; diff --git a/src/test/owr/OptimisticWithdrawalRecipient.t.sol b/src/test/owr/OptimisticWithdrawalRecipient.t.sol index 974fd5c..c777e93 100644 --- a/src/test/owr/OptimisticWithdrawalRecipient.t.sol +++ b/src/test/owr/OptimisticWithdrawalRecipient.t.sol @@ -23,9 +23,9 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { address internal recoveryAddress; OptimisticWithdrawalRecipient owrETH; - OptimisticWithdrawalRecipient owrERC20; + // OptimisticWithdrawalRecipient owrERC20; OptimisticWithdrawalRecipient owrETH_OR; - OptimisticWithdrawalRecipient owrERC20_OR; + // OptimisticWithdrawalRecipient owrERC20_OR; MockERC20 mERC20; address public principalRecipient; @@ -46,16 +46,10 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { recoveryAddress = makeAddr("recoveryAddress"); owrETH = - owrFactory.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, trancheThreshold); - - owrERC20 = owrFactory.createOWRecipient( - address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, trancheThreshold - ); + owrFactory.createOWRecipient(recoveryAddress, principalRecipient, rewardRecipient, trancheThreshold); owrETH_OR = - owrFactory.createOWRecipient(ETH_ADDRESS, address(0), principalRecipient, rewardRecipient, trancheThreshold); - owrERC20_OR = - owrFactory.createOWRecipient(address(mERC20), address(0), principalRecipient, rewardRecipient, trancheThreshold); + owrFactory.createOWRecipient(address(0), principalRecipient, rewardRecipient, trancheThreshold); } function testGetTranches() public { @@ -65,29 +59,16 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(_principalRecipient, principalRecipient, "invalid principal recipient"); assertEq(_rewardRecipient, rewardRecipient, "invalid reward recipient"); assertEq(wtrancheThreshold, ETH_STAKE, "invalid eth tranche threshold"); - - // erc20 - (_principalRecipient, _rewardRecipient, wtrancheThreshold) = owrERC20.getTranches(); - - assertEq(_principalRecipient, principalRecipient, "invalid erc20 principal recipient"); - assertEq(_rewardRecipient, rewardRecipient, "invalid erc20 reward recipient"); - assertEq(wtrancheThreshold, ETH_STAKE, "invalid erc20 tranche threshold"); } function testReceiveETH() public { address(owrETH).safeTransferETH(1 ether); assertEq(address(owrETH).balance, 1 ether); - - address(owrERC20).safeTransferETH(1 ether); - assertEq(address(owrERC20).balance, 1 ether); } function testReceiveTransfer() public { payable(address(owrETH)).transfer(1 ether); assertEq(address(owrETH).balance, 1 ether); - - payable(address(owrERC20)).transfer(1 ether); - assertEq(address(owrERC20).balance, 1 ether); } function testEmitOnReceiveETH() public { @@ -100,9 +81,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { function testReceiveERC20() public { address(mERC20).safeTransfer(address(owrETH), 1 ether); assertEq(mERC20.balanceOf(address(owrETH)), 1 ether); - - address(mERC20).safeTransfer(address(owrERC20), 1 ether); - assertEq(mERC20.balanceOf(address(owrERC20)), 1 ether); } function testCan_recoverNonOWRFundsToRecipient() public { @@ -133,62 +111,23 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(address(owrETH_OR).balance, 1 ether); assertEq(mERC20.balanceOf(address(owrETH_OR)), 0 ether); assertEq(mERC20.balanceOf(rewardRecipient), 1 ether); - - address(owrERC20).safeTransferETH(1 ether); - address(mERC20).safeTransfer(address(owrERC20), 1 ether); - - vm.expectEmit(true, true, true, true); - emit RecoverNonOWRecipientFunds(ETH_ADDRESS, recoveryAddress, 1 ether); - owrERC20.recoverFunds(ETH_ADDRESS, recoveryAddress); - assertEq(mERC20.balanceOf(address(owrERC20)), 1 ether); - assertEq(address(owrERC20).balance, 0 ether); - assertEq(recoveryAddress.balance, 1 ether); - - address(owrERC20_OR).safeTransferETH(1 ether); - address(mERC20).safeTransfer(address(owrERC20_OR), 1 ether); - - vm.expectEmit(true, true, true, true); - emit RecoverNonOWRecipientFunds(ETH_ADDRESS, principalRecipient, 1 ether); - owrERC20_OR.recoverFunds(ETH_ADDRESS, principalRecipient); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 1 ether); - assertEq(address(owrERC20_OR).balance, 0 ether); - assertEq(principalRecipient.balance, 1 ether); - - address(owrERC20_OR).safeTransferETH(1 ether); - - owrERC20_OR.recoverFunds(ETH_ADDRESS, rewardRecipient); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 1 ether); - assertEq(address(owrERC20_OR).balance, 0 ether, "invalid erc20 balance"); - assertEq(rewardRecipient.balance, 1 ether, "invalid eth balance"); } function testCannot_recoverFundsToNonRecipient() public { vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_InvalidRecipient.selector); owrETH.recoverFunds(address(mERC20), address(1)); - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_InvalidRecipient.selector); - owrERC20_OR.recoverFunds(ETH_ADDRESS, address(1)); - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_InvalidRecipient.selector); owrETH_OR.recoverFunds(address(mERC20), address(2)); - - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_InvalidRecipient.selector); - owrERC20_OR.recoverFunds(ETH_ADDRESS, address(2)); } - function testCannot_recoverOWRFunds() public { - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); - owrETH.recoverFunds(ETH_ADDRESS, recoveryAddress); - - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); - owrERC20_OR.recoverFunds(address(mERC20), recoveryAddress); + // function testCannot_recoverOWRFunds() public { + // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); + // owrETH.recoverFunds(address(mERC20), recoveryAddress); - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); - owrETH_OR.recoverFunds(ETH_ADDRESS, address(1)); - - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); - owrERC20_OR.recoverFunds(address(mERC20), address(1)); - } + // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); + // owrETH_OR.recoverFunds(address(mERC20), address(1)); + // } function testCan_OWRIsPayable() public { owrETH.distributeFunds{value: 2 ether}(); @@ -201,9 +140,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { function testCan_distributeToNoRecipients() public { owrETH.distributeFunds(); assertEq(principalRecipient.balance, 0 ether); - - owrERC20_OR.distributeFunds(); - assertEq(mERC20.balanceOf(principalRecipient), 0 ether); } function testCan_emitOnDistributeToNoRecipients() public { @@ -218,7 +154,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { function testCan_distributeToSecondRecipient() public { address(owrETH).safeTransferETH(1 ether); - // uint256[] memory payouts = new uint256[](2); uint256 rewardPayout = 1 ether; uint256 principalPayout; @@ -235,23 +170,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(address(owrETH).balance, 0 ether); assertEq(principalRecipient.balance, 0 ether); assertEq(rewardRecipient.balance, 1 ether); - - address(mERC20).safeTransfer(address(owrERC20_OR), 1 ether); - - rewardPayout = 1 ether; - vm.expectEmit(true, true, true, true); - emit DistributeFunds(principalPayout, rewardPayout, 0); - owrERC20_OR.distributeFunds(); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 1 ether); - - rewardPayout = 0; - vm.expectEmit(true, true, true, true); - emit DistributeFunds(principalPayout, rewardPayout, 0); - owrERC20_OR.distributeFunds(); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(principalRecipient.balance, 0 ether); - assertEq(rewardRecipient.balance, 1 ether); } function testCan_distributeMultipleDepositsToRewardRecipient() public { @@ -264,16 +182,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { owrETH.distributeFunds(); assertEq(address(owrETH).balance, 0 ether); assertEq(rewardRecipient.balance, 1 ether); - - address(mERC20).safeTransfer(address(owrERC20_OR), 0.5 ether); - owrERC20_OR.distributeFunds(); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 0.5 ether); - - address(mERC20).safeTransfer(address(owrERC20_OR), 0.5 ether); - owrERC20_OR.distributeFunds(); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 1 ether); } function testCan_distributeToBothRecipients() public { @@ -288,15 +196,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(address(owrETH).balance, 0 ether); assertEq(principalRecipient.balance, 32 ether); assertEq(rewardRecipient.balance, 4 ether); - - address(mERC20).safeTransfer(address(owrERC20_OR), 36 ether); - - vm.expectEmit(true, true, true, true); - emit DistributeFunds(principalPayout, rewardPayout, 0); - owrERC20_OR.distributeFunds(); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(principalRecipient.balance, 32 ether); - assertEq(rewardRecipient.balance, 4 ether); } function testCan_distributeMultipleDepositsToPrincipalRecipient() public { @@ -309,16 +208,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(address(owrETH).balance, 0 ether); assertEq(principalRecipient.balance, 32 ether); assertEq(rewardRecipient.balance, 0 ether); - - address(mERC20).safeTransfer(address(owrERC20_OR), 16 ether); - owrERC20_OR.distributeFunds(); - - address(mERC20).safeTransfer(address(owrERC20_OR), 16 ether); - owrERC20_OR.distributeFunds(); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(mERC20.balanceOf(principalRecipient), 32 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 0); } function testCannot_distributeTooMuch() public { @@ -331,22 +220,12 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); owrETH.distributeFundsPull(); - - address(mERC20).safeTransfer(address(owrERC20_OR), type(uint128).max); - owrERC20_OR.distributeFunds(); - address(mERC20).safeTransfer(address(owrERC20_OR), 1); - - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); - owrERC20_OR.distributeFunds(); - - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); - owrERC20_OR.distributeFundsPull(); } function testCannot_reenterOWR() public { OWRReentrancy wr = new OWRReentrancy(); - owrETH = owrFactory.createOWRecipient(ETH_ADDRESS, recoveryAddress, address(wr), rewardRecipient, 1 ether); + owrETH = owrFactory.createOWRecipient(recoveryAddress, address(wr), rewardRecipient, 1 ether); address(owrETH).safeTransferETH(33 ether); vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); @@ -369,7 +248,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 32 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 4 ether); - assertEq(owrETH.distributedFunds(), 36 ether); + // assertEq(owrETH.distributedFunds(), 36 ether); assertEq(owrETH.fundsPendingWithdrawal(), 36 ether); owrETH.withdraw(rewardRecipient); @@ -381,7 +260,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 32 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0); - assertEq(owrETH.distributedFunds(), 36 ether); + // assertEq(owrETH.distributedFunds(), 36 ether); assertEq(owrETH.fundsPendingWithdrawal(), 32 ether); owrETH.withdraw(principalRecipient); @@ -393,46 +272,8 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0); assertEq(owrETH.getPullBalance(rewardRecipient), 0); - assertEq(owrETH.distributedFunds(), 36 ether); + // assertEq(owrETH.distributedFunds(), 36 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0 ether); - - // test erc20 - address(mERC20).safeTransfer(address(owrERC20_OR), 36 ether); - owrERC20_OR.distributeFundsPull(); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 36 ether); - assertEq(mERC20.balanceOf(principalRecipient), 0); - assertEq(mERC20.balanceOf(rewardRecipient), 0); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 32 ether); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 4 ether); - - assertEq(owrERC20_OR.distributedFunds(), 36 ether); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 36 ether); - - owrERC20_OR.withdraw(rewardRecipient); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 32 ether); - assertEq(mERC20.balanceOf(principalRecipient), 0 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 4 ether); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 32 ether); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 0 ether); - - assertEq(owrERC20_OR.distributedFunds(), 36 ether); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 32 ether); - - owrERC20_OR.withdraw(principalRecipient); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(mERC20.balanceOf(principalRecipient), 32 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 4 ether); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 0 ether); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 0 ether); - - assertEq(owrERC20_OR.distributedFunds(), 36 ether); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 0 ether); } function testCan_distributePushAndPull() public { @@ -449,7 +290,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0 ether); - assertEq(owrETH.distributedFunds(), 0.5 ether); + // assertEq(owrETH.distributedFunds(), 0.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0 ether); address(owrETH).safeTransferETH(1 ether); @@ -464,7 +305,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - assertEq(owrETH.distributedFunds(), 1.5 ether); + // assertEq(owrETH.distributedFunds(), 1.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); owrETH.distributeFunds(); @@ -476,7 +317,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - assertEq(owrETH.distributedFunds(), 1.5 ether); + // assertEq(owrETH.distributedFunds(), 1.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); owrETH.distributeFundsPull(); @@ -488,7 +329,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - assertEq(owrETH.distributedFunds(), 1.5 ether); + // assertEq(owrETH.distributedFunds(), 1.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); address(owrETH).safeTransferETH(1 ether); @@ -503,7 +344,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - assertEq(owrETH.distributedFunds(), 2.5 ether); + // assertEq(owrETH.distributedFunds(), 2.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); owrETH.withdraw(rewardRecipient); @@ -515,7 +356,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0 ether); - assertEq(owrETH.distributedFunds(), 2.5 ether); + // assertEq(owrETH.distributedFunds(), 2.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0); address(owrETH).safeTransferETH(1 ether); @@ -528,80 +369,8 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0 ether); - assertEq(owrETH.distributedFunds(), 2.5 ether); + // assertEq(owrETH.distributedFunds(), 2.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0 ether); - - // TEST ERC20 - - address(mERC20).safeTransfer(address(owrERC20_OR), 0.5 ether); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0.5 ether); - - owrERC20_OR.distributeFunds(); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether, "1/invalid balance"); - assertEq(mERC20.balanceOf(principalRecipient), 0 ether, "2/invalid tranche 1 recipient balance"); - assertEq(mERC20.balanceOf(rewardRecipient), 0.5 ether, "3/invalid tranche 2 recipient balance - 1"); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 0 ether, "4/invalid pull balance"); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 0 ether, "5/invalid pull balance"); - - assertEq(owrERC20_OR.distributedFunds(), 0.5 ether, "6/invalid distributed funds"); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 0 ether, "7/invalid funds pending withdrawal"); - - address(mERC20).safeTransfer(address(owrERC20_OR), 1 ether); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 1 ether, "8/invalid balance"); - - owrERC20_OR.distributeFundsPull(); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 1 ether, "9/invalid balance"); - assertEq(mERC20.balanceOf(principalRecipient), 0 ether, "10/invalid recipeint balance"); - assertEq(mERC20.balanceOf(rewardRecipient), 0.5 ether, "11/invalid recipient balance"); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 0, "12/invalid recipient pull balance"); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 1 ether, "13/invalid recipient pull balance"); - - assertEq(owrERC20_OR.distributedFunds(), 1.5 ether, "14/invalid distributed funds balance"); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 1 ether, "15/invalid funds pending balance"); - - owrERC20_OR.distributeFundsPull(); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 1 ether, "16/invalid balance"); - assertEq(mERC20.balanceOf(principalRecipient), 0 ether, "17/invalid recipient balance"); - assertEq(mERC20.balanceOf(rewardRecipient), 0.5 ether, "18/invalid recipient balance"); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 0 ether, "19/invalid pull balance"); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 1 ether, "20/invalid pull balance"); - - assertEq(owrERC20_OR.distributedFunds(), 1.5 ether, "21/invalid distributed funds"); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 1 ether, "22/invalid funds pending"); - - /// 3 - address(mERC20).safeTransfer(address(owrERC20_OR), 32 ether); - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 33 ether); - - owrERC20_OR.distributeFunds(); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 1 ether); - assertEq(mERC20.balanceOf(principalRecipient), 32 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 0.5 ether); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 0 ether); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 1 ether); - - assertEq(owrERC20_OR.distributedFunds(), 33.5 ether); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 1 ether); - - owrERC20_OR.withdraw(rewardRecipient); - - assertEq(mERC20.balanceOf(address(owrERC20_OR)), 0 ether); - assertEq(mERC20.balanceOf(principalRecipient), 32 ether); - assertEq(mERC20.balanceOf(rewardRecipient), 1.5 ether); - - assertEq(owrERC20_OR.getPullBalance(principalRecipient), 0 ether); - assertEq(owrERC20_OR.getPullBalance(rewardRecipient), 0 ether); - - assertEq(owrERC20_OR.distributedFunds(), 33.5 ether); - assertEq(owrERC20_OR.fundsPendingWithdrawal(), 0 ether); } function testFuzzCan_distributeDepositsToRecipients( @@ -618,11 +387,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { generateTranches(_recipientsSeed, _thresholdsSeed); owrETH = owrFactory.createOWRecipient( - ETH_ADDRESS, recoveryAddress, _principalRecipient, _rewardRecipient, _trancheThreshold - ); - - owrERC20 = owrFactory.createOWRecipient( - address(mERC20), recoveryAddress, _principalRecipient, _rewardRecipient, _trancheThreshold + recoveryAddress, _principalRecipient, _rewardRecipient, _trancheThreshold ); /// test eth @@ -634,7 +399,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { uint256 _totalETHAmount = uint256(_numDeposits) * uint256(_ethAmount); assertEq(address(owrETH).balance, 0 ether, "invalid balance"); - assertEq(owrETH.distributedFunds(), _totalETHAmount, "undistributed funds"); + // assertEq(owrETH.distributedFunds(), _totalETHAmount, "undistributed funds"); assertEq(owrETH.fundsPendingWithdrawal(), 0 ether, "funds pending withdraw"); if (BALANCE_CLASSIFICATION_THRESHOLD > _totalETHAmount) { @@ -661,46 +426,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(_rewardRecipient.balance, 0, "should not classify principal as reward"); } } - - // test erc20 - - for (uint256 i = 0; i < _numDeposits; i++) { - address(mERC20).safeTransfer(address(owrERC20), _erc20Amount); - owrERC20.distributeFunds(); - } - - uint256 _totalERC20Amount = uint256(_numDeposits) * uint256(_erc20Amount); - - assertEq(mERC20.balanceOf(address(owrERC20)), 0 ether, "invalid erc20 balance"); - assertEq(owrERC20.distributedFunds(), _totalERC20Amount, "incorrect distributed funds"); - assertEq(owrERC20.fundsPendingWithdrawal(), 0 ether, "invalid funds pending withdrawal"); - - if (BALANCE_CLASSIFICATION_THRESHOLD > _totalERC20Amount) { - // then all of the deposit should be classified as reward - assertEq(mERC20.balanceOf(_principalRecipient), 0, "should not classify reward as principal"); - - assertEq(mERC20.balanceOf(_rewardRecipient), _totalERC20Amount, "invalid amount reward classification"); - } - - if (_erc20Amount > BALANCE_CLASSIFICATION_THRESHOLD) { - // then all of reward classified as principal - // but check if _totalERC20Amount > first threshold - if (_totalERC20Amount > _trancheThreshold) { - // there is reward - assertEq(mERC20.balanceOf(_principalRecipient), _trancheThreshold, "invalid amount principal classification"); - - assertEq( - mERC20.balanceOf(_rewardRecipient), - _totalERC20Amount - _trancheThreshold, - "should not classify principal as reward" - ); - } else { - // eelse no rewards - assertEq(mERC20.balanceOf(_principalRecipient), _totalERC20Amount, "invalid amount"); - - assertEq(mERC20.balanceOf(_rewardRecipient), 0, "should not classify principal as reward"); - } - } } function testFuzzCan_distributePullDepositsToRecipients( @@ -718,10 +443,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { generateTranches(_recipientsSeed, _thresholdsSeed); owrETH = owrFactory.createOWRecipient( - ETH_ADDRESS, recoveryAddress, _principalRecipient, _rewardRecipient, _trancheThreshold - ); - owrERC20 = owrFactory.createOWRecipient( - address(mERC20), recoveryAddress, _principalRecipient, _rewardRecipient, _trancheThreshold + recoveryAddress, _principalRecipient, _rewardRecipient, _trancheThreshold ); /// test eth @@ -733,7 +455,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { uint256 _totalETHAmount = uint256(_numDeposits) * uint256(_ethAmount); assertEq(address(owrETH).balance, _totalETHAmount); - assertEq(owrETH.distributedFunds(), _totalETHAmount); + // assertEq(owrETH.distributedFunds(), _totalETHAmount); assertEq(owrETH.fundsPendingWithdrawal(), _totalETHAmount); uint256 principal = owrETH.getPullBalance(_principalRecipient); @@ -758,50 +480,10 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { owrETH.withdraw(_rewardRecipient); assertEq(address(owrETH).balance, 0); - assertEq(owrETH.distributedFunds(), _totalETHAmount); + // assertEq(owrETH.distributedFunds(), _totalETHAmount); assertEq(owrETH.fundsPendingWithdrawal(), 0); assertEq(_principalRecipient.balance, principal, "10/invalid principal balance"); assertEq(_rewardRecipient.balance, reward, "11/invalid reward balance"); - - /// test erc20 - - for (uint256 i = 0; i < _numDeposits; i++) { - address(mERC20).safeTransfer(address(owrERC20), _erc20Amount); - owrERC20.distributeFundsPull(); - } - uint256 _totalERC20Amount = uint256(_numDeposits) * uint256(_erc20Amount); - - assertEq(mERC20.balanceOf(address(owrERC20)), _totalERC20Amount); - assertEq(owrERC20.distributedFunds(), _totalERC20Amount); - assertEq(owrERC20.fundsPendingWithdrawal(), _totalERC20Amount); - - principal = owrERC20.getPullBalance(_principalRecipient); - assertEq( - owrERC20.getPullBalance(_principalRecipient), - (_erc20Amount >= BALANCE_CLASSIFICATION_THRESHOLD) - ? _trancheThreshold > _totalERC20Amount ? _totalERC20Amount : _trancheThreshold - : 0, - "16/invalid recipient balance" - ); - - reward = owrERC20.getPullBalance(_rewardRecipient); - assertEq( - owrERC20.getPullBalance(_rewardRecipient), - (_erc20Amount >= BALANCE_CLASSIFICATION_THRESHOLD) - ? _totalERC20Amount > _trancheThreshold ? (_totalERC20Amount - _trancheThreshold) : 0 - : _totalERC20Amount, - "17/invalid recipient balance" - ); - - owrERC20.withdraw(_principalRecipient); - owrERC20.withdraw(_rewardRecipient); - - assertEq(mERC20.balanceOf(address(owrERC20)), 0, "18/invalid balance"); - assertEq(owrERC20.distributedFunds(), _totalERC20Amount, "19/invalid balance"); - assertEq(owrERC20.fundsPendingWithdrawal(), 0, "20/invalid funds pending"); - - assertEq(mERC20.balanceOf(_principalRecipient), principal, "21/invalid principal balance"); - assertEq(mERC20.balanceOf(_rewardRecipient), reward, "22/invalid reward balance"); } } diff --git a/src/test/owr/OptimisticWithdrawalRecipientFactory.t.sol b/src/test/owr/OptimisticWithdrawalRecipientFactory.t.sol index fd02690..41f390c 100644 --- a/src/test/owr/OptimisticWithdrawalRecipientFactory.t.sol +++ b/src/test/owr/OptimisticWithdrawalRecipientFactory.t.sol @@ -10,7 +10,6 @@ import {OWRTestHelper} from "./OWRTestHelper.t.sol"; contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { event CreateOWRecipient( address indexed owr, - address token, address recoveryAddress, address principalRecipient, address rewardRecipient, @@ -18,7 +17,6 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { ); OptimisticWithdrawalRecipientFactory owrFactoryModule; - MockERC20 mERC20; address public recoveryAddress; address public principalRecipient; @@ -26,9 +24,6 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { uint256 public threshold; function setUp() public { - mERC20 = new MockERC20("Test Token", "TOK", 18); - mERC20.mint(type(uint256).max); - owrFactoryModule = new OptimisticWithdrawalRecipientFactory(); recoveryAddress = makeAddr("recoveryAddress"); @@ -37,69 +32,41 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { } function testCan_createOWRecipient() public { - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); - - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, principalRecipient, rewardRecipient, threshold); recoveryAddress = address(0); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); - - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, principalRecipient, rewardRecipient, threshold); } function testCan_emitOnCreate() public { // don't check deploy address vm.expectEmit(false, true, true, true); emit CreateOWRecipient( - address(0xdead), ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold - ); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); - - // don't check deploy address - vm.expectEmit(false, true, true, true); - emit CreateOWRecipient( - address(0xdead), address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold + address(0xdead), recoveryAddress, principalRecipient, rewardRecipient, threshold ); - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, principalRecipient, rewardRecipient, threshold); recoveryAddress = address(0); // don't check deploy address vm.expectEmit(false, true, true, true); emit CreateOWRecipient( - address(0xdead), ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold + address(0xdead), recoveryAddress, principalRecipient, rewardRecipient, threshold ); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); - - // don't check deploy address - vm.expectEmit(false, true, true, true); - emit CreateOWRecipient( - address(0xdead), address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold - ); - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, principalRecipient, rewardRecipient, threshold); } function testCannot_createWithInvalidRecipients() public { (principalRecipient, rewardRecipient, threshold) = generateTranches(1, 1); // eth vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__Recipients.selector); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, address(0), rewardRecipient, threshold); - - vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__Recipients.selector); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, address(0), address(0), threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, address(0), rewardRecipient, threshold); vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__Recipients.selector); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, address(0), threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, address(0), address(0), threshold); - // erc20 vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__Recipients.selector); - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, address(0), rewardRecipient, threshold); - - vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__Recipients.selector); - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, address(0), address(0), threshold); - - vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__Recipients.selector); - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, address(0), threshold); + owrFactoryModule.createOWRecipient( recoveryAddress, principalRecipient, address(0), threshold); } function testCannot_createWithInvalidThreshold() public { @@ -107,7 +74,7 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { threshold = 0; vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__ZeroThreshold.selector); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient( recoveryAddress, principalRecipient, rewardRecipient, threshold); vm.expectRevert( abi.encodeWithSelector( @@ -115,7 +82,7 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { ) ); owrFactoryModule.createOWRecipient( - ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, type(uint128).max + recoveryAddress, principalRecipient, rewardRecipient, type(uint128).max ); } @@ -132,15 +99,9 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { vm.expectEmit(false, true, true, true); emit CreateOWRecipient( - address(0xdead), ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold - ); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); - - vm.expectEmit(false, true, true, true); - emit CreateOWRecipient( - address(0xdead), address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold + address(0xdead), recoveryAddress, principalRecipient, rewardRecipient, threshold ); - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, principalRecipient, rewardRecipient, threshold); } function testFuzzCannot_CreateWithZeroThreshold(uint256 _receipientSeed) public { @@ -149,12 +110,7 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { // eth vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__ZeroThreshold.selector); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); - - // erc20 - vm.expectRevert(OptimisticWithdrawalRecipientFactory.Invalid__ZeroThreshold.selector); - - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient(recoveryAddress, principalRecipient, rewardRecipient, threshold); } function testFuzzCannot_CreateWithLargeThreshold(uint256 _receipientSeed, uint256 _threshold) public { @@ -167,12 +123,6 @@ contract OptimisticWithdrawalRecipientFactoryTest is OWRTestHelper, Test { abi.encodeWithSelector(OptimisticWithdrawalRecipientFactory.Invalid__ThresholdTooLarge.selector, _threshold) ); - owrFactoryModule.createOWRecipient(ETH_ADDRESS, recoveryAddress, principalRecipient, rewardRecipient, threshold); - - vm.expectRevert( - abi.encodeWithSelector(OptimisticWithdrawalRecipientFactory.Invalid__ThresholdTooLarge.selector, _threshold) - ); - - owrFactoryModule.createOWRecipient(address(mERC20), recoveryAddress, principalRecipient, rewardRecipient, threshold); + owrFactoryModule.createOWRecipient( recoveryAddress, principalRecipient, rewardRecipient, threshold); } } From a3b8e5fcc22639acbdd3c5f5b2b39b26c60e6ad9 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Mon, 25 Sep 2023 00:00:10 +0100 Subject: [PATCH 08/12] mend --- .../owr/OptimisticWithdrawalRecipient.t.sol | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/test/owr/OptimisticWithdrawalRecipient.t.sol b/src/test/owr/OptimisticWithdrawalRecipient.t.sol index c777e93..ba85595 100644 --- a/src/test/owr/OptimisticWithdrawalRecipient.t.sol +++ b/src/test/owr/OptimisticWithdrawalRecipient.t.sol @@ -23,9 +23,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { address internal recoveryAddress; OptimisticWithdrawalRecipient owrETH; - // OptimisticWithdrawalRecipient owrERC20; OptimisticWithdrawalRecipient owrETH_OR; - // OptimisticWithdrawalRecipient owrERC20_OR; MockERC20 mERC20; address public principalRecipient; @@ -210,17 +208,17 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(rewardRecipient.balance, 0 ether); } - function testCannot_distributeTooMuch() public { - vm.deal(address(owrETH), type(uint128).max); - owrETH.distributeFunds(); - vm.deal(address(owrETH), 1); + // function testCannot_distributeTooMuch() public { + // vm.deal(address(owrETH), type(uint128).max); + // owrETH.distributeFunds(); + // vm.deal(address(owrETH), 1); - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); - owrETH.distributeFunds(); + // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); + // owrETH.distributeFunds(); - vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); - owrETH.distributeFundsPull(); - } + // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); + // owrETH.distributeFundsPull(); + // } function testCannot_reenterOWR() public { OWRReentrancy wr = new OWRReentrancy(); @@ -248,7 +246,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 32 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 4 ether); - // assertEq(owrETH.distributedFunds(), 36 ether); assertEq(owrETH.fundsPendingWithdrawal(), 36 ether); owrETH.withdraw(rewardRecipient); @@ -260,7 +257,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 32 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0); - // assertEq(owrETH.distributedFunds(), 36 ether); assertEq(owrETH.fundsPendingWithdrawal(), 32 ether); owrETH.withdraw(principalRecipient); @@ -272,7 +268,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0); assertEq(owrETH.getPullBalance(rewardRecipient), 0); - // assertEq(owrETH.distributedFunds(), 36 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0 ether); } @@ -290,7 +285,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0 ether); - // assertEq(owrETH.distributedFunds(), 0.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0 ether); address(owrETH).safeTransferETH(1 ether); @@ -305,7 +299,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - // assertEq(owrETH.distributedFunds(), 1.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); owrETH.distributeFunds(); @@ -317,7 +310,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - // assertEq(owrETH.distributedFunds(), 1.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); owrETH.distributeFundsPull(); @@ -329,7 +321,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - // assertEq(owrETH.distributedFunds(), 1.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); address(owrETH).safeTransferETH(1 ether); @@ -344,7 +335,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 1 ether); - // assertEq(owrETH.distributedFunds(), 2.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 1 ether); owrETH.withdraw(rewardRecipient); @@ -356,7 +346,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0 ether); - // assertEq(owrETH.distributedFunds(), 2.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0); address(owrETH).safeTransferETH(1 ether); @@ -369,7 +358,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(owrETH.getPullBalance(principalRecipient), 0 ether); assertEq(owrETH.getPullBalance(rewardRecipient), 0 ether); - // assertEq(owrETH.distributedFunds(), 2.5 ether); assertEq(owrETH.fundsPendingWithdrawal(), 0 ether); } @@ -480,7 +468,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { owrETH.withdraw(_rewardRecipient); assertEq(address(owrETH).balance, 0); - // assertEq(owrETH.distributedFunds(), _totalETHAmount); assertEq(owrETH.fundsPendingWithdrawal(), 0); assertEq(_principalRecipient.balance, principal, "10/invalid principal balance"); From be13b7d2187a9408038b1819f2463783395e0199 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Wed, 27 Sep 2023 06:54:08 +0100 Subject: [PATCH 09/12] chore: add _fundsToBeDistributed check; test: add OWR distribution too large check --- src/owr/OptimisticWithdrawalRecipient.sol | 1 + .../owr/OptimisticWithdrawalRecipient.t.sol | 26 +++++++------------ 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/owr/OptimisticWithdrawalRecipient.sol b/src/owr/OptimisticWithdrawalRecipient.sol index 6d4a697..f246cac 100644 --- a/src/owr/OptimisticWithdrawalRecipient.sol +++ b/src/owr/OptimisticWithdrawalRecipient.sol @@ -275,6 +275,7 @@ contract OptimisticWithdrawalRecipient is Clone { } { + if (_fundsToBeDistributed > type(uint128).max) revert InvalidDistribution_TooLarge(); // Write to storage // the principal value claimedPrincipalFunds += _principalPayout; diff --git a/src/test/owr/OptimisticWithdrawalRecipient.t.sol b/src/test/owr/OptimisticWithdrawalRecipient.t.sol index ba85595..9d51f5c 100644 --- a/src/test/owr/OptimisticWithdrawalRecipient.t.sol +++ b/src/test/owr/OptimisticWithdrawalRecipient.t.sol @@ -119,14 +119,6 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { owrETH_OR.recoverFunds(address(mERC20), address(2)); } - // function testCannot_recoverOWRFunds() public { - // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); - // owrETH.recoverFunds(address(mERC20), recoveryAddress); - - // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidTokenRecovery_OWRToken.selector); - // owrETH_OR.recoverFunds(address(mERC20), address(1)); - // } - function testCan_OWRIsPayable() public { owrETH.distributeFunds{value: 2 ether}(); @@ -208,17 +200,17 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { assertEq(rewardRecipient.balance, 0 ether); } - // function testCannot_distributeTooMuch() public { - // vm.deal(address(owrETH), type(uint128).max); - // owrETH.distributeFunds(); - // vm.deal(address(owrETH), 1); + function testCannot_distributeTooMuch() public { + vm.deal(address(owrETH), type(uint128).max); + owrETH.distributeFunds(); + vm.deal(address(owrETH), 1); - // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); - // owrETH.distributeFunds(); + vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); + owrETH.distributeFunds(); - // vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); - // owrETH.distributeFundsPull(); - // } + vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); + owrETH.distributeFundsPull(); + } function testCannot_reenterOWR() public { OWRReentrancy wr = new OWRReentrancy(); From 3454c9137aba5ca6a179538b1bdc3a8ff26bec5a Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Wed, 27 Sep 2023 07:05:14 +0100 Subject: [PATCH 10/12] test: fix testCannot_distributeTooMuch test case --- src/test/owr/OptimisticWithdrawalRecipient.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/owr/OptimisticWithdrawalRecipient.t.sol b/src/test/owr/OptimisticWithdrawalRecipient.t.sol index 9d51f5c..8422559 100644 --- a/src/test/owr/OptimisticWithdrawalRecipient.t.sol +++ b/src/test/owr/OptimisticWithdrawalRecipient.t.sol @@ -205,6 +205,7 @@ contract OptimisticWithdrawalRecipientTest is OWRTestHelper, Test { owrETH.distributeFunds(); vm.deal(address(owrETH), 1); + vm.deal(address(owrETH), type(uint136).max); vm.expectRevert(OptimisticWithdrawalRecipient.InvalidDistribution_TooLarge.selector); owrETH.distributeFunds(); From fa5ad3304421376c882748f2c1fcd643dd346ddd Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:03:47 +0100 Subject: [PATCH 11/12] chore: add remapping, update solady module version --- .gitmodules | 2 +- foundry.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index a897cff..4a0b298 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,7 +8,7 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/vectorized/solady - branch = v0.0.92 + branch = v0.0.123 [submodule "lib/splits-utils"] path = lib/splits-utils url = https://github.com/0xSplits/splits-utils diff --git a/foundry.toml b/foundry.toml index 84f844b..48cd2d6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,6 +6,7 @@ remappings = [ 'ds-test/=lib/ds-test/src/', 'solmate/=lib/solmate/src/', 'splits-tests/=lib/splits-utils/test/', + 'solady/=lib/solady/src/', ] solc_version = '0.8.19' From 012a543f242c4706876cbd585fa854ff93be1a53 Mon Sep 17 00:00:00 2001 From: samparsky <8148384+samparsky@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:28:33 +0100 Subject: [PATCH 12/12] chore: update Lido.rescueFunds natspec; change transfer to safetransfer --- src/lido/LidoSplit.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lido/LidoSplit.sol b/src/lido/LidoSplit.sol index ef69379..6457e34 100644 --- a/src/lido/LidoSplit.sol +++ b/src/lido/LidoSplit.sol @@ -70,9 +70,9 @@ contract LidoSplit is Clone { ERC20(wstETH).safeTransfer(splitWallet(), amount); } - /// @notice Rescue stuck ETH + /// @notice Rescue stuck ETH and tokens /// Uses token == address(0) to represent ETH - /// @return balance Amount of ETH rescued + /// @return balance Amount of ETH or tokens rescued function rescueFunds(address token) external returns (uint256 balance) { if (token == address(stETH)) revert Invalid_Address(); @@ -81,7 +81,7 @@ contract LidoSplit is Clone { if (balance > 0) splitWallet().safeTransferETH(balance); } else { balance = ERC20(token).balanceOf(address(this)); - if (balance > 0) ERC20(token).transfer(splitWallet(), balance); + if (balance > 0) ERC20(token).safeTransfer(splitWallet(), balance); } } }