diff --git a/packages/ethereum-contracts/contracts/utils/BatchLiquidator.sol b/packages/ethereum-contracts/contracts/utils/BatchLiquidator.sol index 012f641a83..3c6b063e1f 100644 --- a/packages/ethereum-contracts/contracts/utils/BatchLiquidator.sol +++ b/packages/ethereum-contracts/contracts/utils/BatchLiquidator.sol @@ -70,7 +70,10 @@ contract BatchLiquidator { { uint256 balance = ERC20(superToken).balanceOf(address(this)); if (balance > 0) { - ERC20(superToken).transferFrom(address(this), msg.sender, balance); + // don't fail for non-transferrable tokens + try ERC20(superToken).transferFrom(address(this), msg.sender, balance) + // solhint-disable-next-line no-empty-blocks + {} catch {} } } } @@ -110,7 +113,9 @@ contract BatchLiquidator { { uint256 balance = ERC20(superToken).balanceOf(address(this)); if (balance > 0) { - ERC20(superToken).transferFrom(address(this), msg.sender, balance); + try ERC20(superToken).transferFrom(address(this), msg.sender, balance) + // solhint-disable-next-line no-empty-blocks + {} catch {} } } } diff --git a/packages/ethereum-contracts/test/foundry/utils/BatchLiquidator.t.sol b/packages/ethereum-contracts/test/foundry/utils/BatchLiquidator.t.sol index c1a7b299a3..dfaeea61cf 100644 --- a/packages/ethereum-contracts/test/foundry/utils/BatchLiquidator.t.sol +++ b/packages/ethereum-contracts/test/foundry/utils/BatchLiquidator.t.sol @@ -2,8 +2,30 @@ pragma solidity 0.8.19; import { FoundrySuperfluidTester, SuperTokenV1Library } from "../FoundrySuperfluidTester.sol"; -import { ISuperToken } from "../../../contracts/superfluid/SuperToken.sol"; +import { ISuperToken, SuperToken, ISuperfluid, IConstantOutflowNFT, IConstantInflowNFT } from "../../../contracts/superfluid/SuperToken.sol"; import { BatchLiquidator } from "../../../contracts/utils/BatchLiquidator.sol"; +import "forge-std/Test.sol"; + +contract NonTransferableST is SuperToken { + // transferFrom will always revert + constructor( + ISuperfluid host + ) + SuperToken(host, IConstantOutflowNFT(address(0)), IConstantInflowNFT(address(0))) // solhint-disable-next-line no-empty-blocks + { + } + + function transferFrom(address holder, address recipient, uint256 amount) public override returns (bool) { + revert(); + } + + function mintInternal( + address to, + uint256 amount + ) external { + _mint(msg.sender, to, amount, false /* invokeHook */, false /* requireReceptionAck */, "", ""); + } +} contract BatchLiquidatorTest is FoundrySuperfluidTester { using SuperTokenV1Library for ISuperToken; @@ -13,11 +35,14 @@ contract BatchLiquidatorTest is FoundrySuperfluidTester { int96 internal constant FLOW_RATE = 10000000000; + ISuperToken badToken; + constructor() FoundrySuperfluidTester(5) { } function setUp() public override { super.setUp(); batchLiquidator = new BatchLiquidator(address(sf.host), address(sf.cfa)); + badToken = new NonTransferableST(sf.host); } // Helpers @@ -129,4 +154,54 @@ contract BatchLiquidatorTest is FoundrySuperfluidTester { ); vm.stopPrank(); } + + function testLiquidationWithCustomTokenRevert() public { + NonTransferableST(address(badToken)).mintInternal(alice, 10 ether); + + vm.startPrank(alice); + badToken.createFlow(bob, FLOW_RATE); + badToken.transferAll(admin); + vm.warp(4 hours); // jump 4 hours + vm.stopPrank(); + vm.startPrank(liquidator); + + batchLiquidator.deleteFlow(address(badToken), alice, bob); + _assertNoFlow(alice, bob); + + assertTrue( + superToken.balanceOf(liquidator) == 0, "BatchLiquidator: SL - Balance should be 0 because of revert" + ); + vm.stopPrank(); + + } + + function testBatchLiquidationWithCustomTokenRevert() public { + NonTransferableST(address(badToken)).mintInternal(alice, 10 ether); + NonTransferableST(address(badToken)).mintInternal(bob, 10 ether); + + vm.startPrank(alice); + badToken.createFlow(bob, FLOW_RATE); + badToken.transferAll(admin); + vm.stopPrank(); + + vm.startPrank(bob); + badToken.createFlow(carol, FLOW_RATE); + badToken.transferAll(admin); + vm.stopPrank(); + + vm.warp(4 hours); // jump 4 hours + + vm.startPrank(liquidator); + + address[] memory senders = new address[](2); + address[] memory receivers = new address[](2); + senders[0] = alice; + senders[1] = bob; + receivers[0] = bob; + receivers[1] = carol; + + batchLiquidator.deleteFlows(address(superToken), senders, receivers); + _assertNoFlow(alice, bob); + _assertNoFlow(bob, carol); + } }