From ab3f0eb1cb63a7b8473aa91555366ba349f02ab9 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Tue, 31 Dec 2024 19:31:18 +0530 Subject: [PATCH] refactor: batch function --- bun.lockb | Bin 63162 -> 63628 bytes package.json | 3 +- remappings.txt | 5 +- src/SablierFlow.sol | 2 +- src/abstracts/Batch.sol | 26 --- src/interfaces/IBatch.sol | 9 - src/interfaces/ISablierFlow.sol | 2 +- tests/fork/Fork.t.sol | 1 - tests/integration/concrete/batch/batch.t.sol | 211 +++---------------- 9 files changed, 42 insertions(+), 217 deletions(-) delete mode 100644 src/abstracts/Batch.sol delete mode 100644 src/interfaces/IBatch.sol diff --git a/bun.lockb b/bun.lockb index 4ab3611c1e1c5e208774835d4e7b7f212592ef92..11e0967ce711410440b003f506452c761d88c89a 100755 GIT binary patch delta 11092 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 Rx9*dFJDb?>!m0NM{x`^7`NseN diff --git a/package.json b/package.json index 5fc3d496..651d2e44 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ }, "dependencies": { "@openzeppelin/contracts": "5.0.2", - "@prb/math": "4.1.0" + "@prb/math": "4.1.0", + "@sablier/lockup": "github:sablier-labs/v2-core#f890214" }, "devDependencies": { "forge-std": "github:foundry-rs/forge-std#v1.8.2", diff --git a/remappings.txt b/remappings.txt index 89f80792..b3c6fa7b 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,5 @@ @openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ @prb/math/=node_modules/@prb/math/ -forge-std/=node_modules/forge-std/ -solady/=node_modules/solady/ \ No newline at end of file +@sablier/lockup/=node_modules/@sablier/lockup/ +forge-std/=node_modules/forge-std/ +solady/=node_modules/solady/ diff --git a/src/SablierFlow.sol b/src/SablierFlow.sol index 350101a7..48164f7c 100644 --- a/src/SablierFlow.sol +++ b/src/SablierFlow.sol @@ -8,8 +8,8 @@ import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import { ud21x18, UD21x18 } from "@prb/math/src/UD21x18.sol"; import { UD60x18, ZERO } from "@prb/math/src/UD60x18.sol"; +import { Batch } from "@sablier/lockup/src/abstracts/Batch.sol"; -import { Batch } from "./abstracts/Batch.sol"; import { NoDelegateCall } from "./abstracts/NoDelegateCall.sol"; import { SablierFlowBase } from "./abstracts/SablierFlowBase.sol"; import { IFlowNFTDescriptor } from "./interfaces/IFlowNFTDescriptor.sol"; diff --git a/src/abstracts/Batch.sol b/src/abstracts/Batch.sol deleted file mode 100644 index d6d2f7e0..00000000 --- a/src/abstracts/Batch.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.22; - -import { IBatch } from "../interfaces/IBatch.sol"; -import { Errors } from "../libraries/Errors.sol"; - -/// @title Batch -/// @notice See the documentation in {IBatch}. -/// @dev Forked from: https://github.com/boringcrypto/BoringSolidity/blob/master/contracts/BoringBatchable.sol -abstract contract Batch is IBatch { - /*////////////////////////////////////////////////////////////////////////// - USER-FACING NON-CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @inheritdoc IBatch - function batch(bytes[] calldata calls) external payable override { - uint256 count = calls.length; - - for (uint256 i = 0; i < count; ++i) { - (bool success, bytes memory result) = address(this).delegatecall(calls[i]); - if (!success) { - revert Errors.BatchError(result); - } - } - } -} diff --git a/src/interfaces/IBatch.sol b/src/interfaces/IBatch.sol deleted file mode 100644 index 862762ef..00000000 --- a/src/interfaces/IBatch.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.22; - -/// @notice This contract implements logic to batch call any function. -interface IBatch { - /// @notice Allows batched call to self, `this` contract. - /// @param calls An array of inputs for each call. - function batch(bytes[] calldata calls) external payable; -} diff --git a/src/interfaces/ISablierFlow.sol b/src/interfaces/ISablierFlow.sol index 593cb8cb..41f91c78 100644 --- a/src/interfaces/ISablierFlow.sol +++ b/src/interfaces/ISablierFlow.sol @@ -3,9 +3,9 @@ pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UD21x18 } from "@prb/math/src/UD21x18.sol"; +import { IBatch } from "@sablier/lockup/src/interfaces/IBatch.sol"; import { Broker, Flow } from "./../types/DataTypes.sol"; -import { IBatch } from "./IBatch.sol"; import { ISablierFlowBase } from "./ISablierFlowBase.sol"; /// @title ISablierFlow diff --git a/tests/fork/Fork.t.sol b/tests/fork/Fork.t.sol index 20b050ec..6b390d15 100644 --- a/tests/fork/Fork.t.sol +++ b/tests/fork/Fork.t.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.22; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { ISablierFlow } from "src/interfaces/ISablierFlow.sol"; import { SablierFlow } from "src/SablierFlow.sol"; import { Base_Test } from "../Base.t.sol"; diff --git a/tests/integration/concrete/batch/batch.t.sol b/tests/integration/concrete/batch/batch.t.sol index 14e63c55..4994cd60 100644 --- a/tests/integration/concrete/batch/batch.t.sol +++ b/tests/integration/concrete/batch/batch.t.sol @@ -2,7 +2,6 @@ pragma solidity >=0.8.22; import { IERC4906 } from "@openzeppelin/contracts/interfaces/IERC4906.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud21x18, UD21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "src/interfaces/ISablierFlow.sol"; import { Errors } from "src/libraries/Errors.sol"; @@ -13,67 +12,25 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { function setUp() public override { Shared_Integration_Concrete_Test.setUp(); - defaultStreamIds.push(defaultStreamId); - // Create a second stream - vm.warp({ newTimestamp: getBlockTimestamp() - ONE_MONTH }); + // The first stream is the default stream. + defaultStreamIds.push(defaultStreamId); + // Create a new stream as the second stream. defaultStreamIds.push(createDefaultStream()); - - vm.warp({ newTimestamp: WARP_ONE_MONTH }); } /*////////////////////////////////////////////////////////////////////////// REVERT //////////////////////////////////////////////////////////////////////////*/ - function test_RevertWhen_CustomError() external { - // The calls declared as bytes. - bytes[] memory calls = new bytes[](1); - calls[0] = abi.encodeCall(flow.withdrawMax, (1, users.recipient)); - - bytes memory expectedRevertData = abi.encodeWithSelector( - Errors.BatchError.selector, abi.encodeWithSelector(Errors.SablierFlow_WithdrawAmountZero.selector, 1) - ); - - vm.expectRevert(expectedRevertData); - flow.batch(calls); - } - - function test_RevertWhen_StringMessage() external { - uint256 streamId = flow.create({ - sender: users.sender, - recipient: users.recipient, - ratePerSecond: RATE_PER_SECOND, - token: IERC20(address(usdt)), - transferable: TRANSFERABLE - }); - - address noAllowanceAddress = address(0xBEEF); - resetPrank({ msgSender: noAllowanceAddress }); - - // The calls declared as bytes. - bytes[] memory calls = new bytes[](1); - calls[0] = abi.encodeCall(flow.deposit, (streamId, DEPOSIT_AMOUNT_6D, users.sender, users.recipient)); - - bytes memory expectedRevertData = abi.encodeWithSelector( - Errors.BatchError.selector, abi.encodeWithSignature("Error(string)", "ERC20: insufficient allowance") - ); - - vm.expectRevert(expectedRevertData); - flow.batch(calls); - } - - function test_RevertWhen_SilentRevert() external { - uint256 streamId = createDefaultStream(IERC20(address(usdt))); - - // The calls declared as bytes - bytes[] memory calls = new bytes[](1); - calls[0] = abi.encodeCall(flow.refund, (streamId, REFUND_AMOUNT_6D)); - - // Remove the ERC-20 balance from flow contract. - deal({ token: address(usdt), to: address(flow), give: 0 }); + /// @dev The batch call pauses a null stream. + function test_RevertWhen_FlowThrows() external { + bytes[] memory calls = new bytes[](2); + calls[0] = abi.encodeCall(flow.pause, (defaultStreamId)); + calls[1] = abi.encodeCall(flow.pause, (nullStreamId)); - vm.expectRevert(); + // It should revert on nullStreamId. + vm.expectRevert(abi.encodeWithSelector(Errors.SablierFlow_Null.selector, nullStreamId)); flow.batch(calls); } @@ -82,18 +39,13 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { //////////////////////////////////////////////////////////////////////////*/ function test_Batch_AdjustRatePerSecond() external { - depositDefaultAmount(defaultStreamIds[0]); - depositDefaultAmount(defaultStreamIds[1]); - UD21x18 newRatePerSecond = ud21x18(RATE_PER_SECOND.unwrap() + 1); bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(flow.adjustRatePerSecond, (defaultStreamIds[0], newRatePerSecond)); calls[1] = abi.encodeCall(flow.adjustRatePerSecond, (defaultStreamIds[1], newRatePerSecond)); - // It should emit 2 {AdjustRatePerSecond} and 2 {MetadataUpdate} events. - - // First stream to adjust rate per second + // It should emit 2 {AdjustRatePerSecond} events. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.AdjustFlowStream({ streamId: defaultStreamIds[0], @@ -101,23 +53,14 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { oldRatePerSecond: RATE_PER_SECOND, newRatePerSecond: newRatePerSecond }); - - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[0] }); - - // Second stream to adjust rate per second vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.AdjustFlowStream({ streamId: defaultStreamIds[1], - totalDebt: ONE_MONTH_DEBT_6D, + totalDebt: 0, oldRatePerSecond: RATE_PER_SECOND, newRatePerSecond: newRatePerSecond }); - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[1] }); - - // Call the batch function. flow.batch(calls); } @@ -126,47 +69,17 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { //////////////////////////////////////////////////////////////////////////*/ function test_Batch_Create() external { - uint256[] memory expectedStreamIds = new uint256[](2); - expectedStreamIds[0] = flow.nextStreamId(); - expectedStreamIds[1] = flow.nextStreamId() + 1; + uint256 expectedNextStreamId = flow.nextStreamId(); - // The calls declared as bytes. bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(flow.create, (users.sender, users.recipient, RATE_PER_SECOND, usdc, TRANSFERABLE)); calls[1] = abi.encodeCall(flow.create, (users.sender, users.recipient, RATE_PER_SECOND, usdc, TRANSFERABLE)); - // It should emit 2 {MetadataUpdate} and 2 {CreateFlowStream} events. - - // First stream to create. - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: expectedStreamIds[0] }); - - vm.expectEmit({ emitter: address(flow) }); - emit ISablierFlow.CreateFlowStream({ - streamId: expectedStreamIds[0], - sender: users.sender, - recipient: users.recipient, - ratePerSecond: RATE_PER_SECOND, - token: usdc, - transferable: TRANSFERABLE - }); - - // Second stream to create. - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: expectedStreamIds[1] }); - - vm.expectEmit({ emitter: address(flow) }); - emit ISablierFlow.CreateFlowStream({ - streamId: expectedStreamIds[1], - sender: users.sender, - recipient: users.recipient, - ratePerSecond: RATE_PER_SECOND, - token: usdc, - transferable: TRANSFERABLE - }); - // Call the batch function. - flow.batch(calls); + bytes[] memory results = flow.batch(calls); + assertEq(results.length, 2, "batch results length"); + assertEq(abi.decode(results[0], (uint256)), expectedNextStreamId, "batch results[0]"); + assertEq(abi.decode(results[1], (uint256)), expectedNextStreamId + 1, "batch results[1]"); } /*////////////////////////////////////////////////////////////////////////// @@ -174,31 +87,17 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { //////////////////////////////////////////////////////////////////////////*/ function test_Batch_Deposit() external { - // The calls declared as bytes. bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(flow.deposit, (defaultStreamIds[0], DEPOSIT_AMOUNT_6D, users.sender, users.recipient)); calls[1] = abi.encodeCall(flow.deposit, (defaultStreamIds[1], DEPOSIT_AMOUNT_6D, users.sender, users.recipient)); - // It should emit 2 {Transfer}, 2 {DepositFlowStream}, 2 {MetadataUpdate} events. - - // First stream to deposit. - vm.expectEmit({ emitter: address(usdc) }); - emit IERC20.Transfer({ from: users.sender, to: address(flow), value: DEPOSIT_AMOUNT_6D }); - + // It should emit 2 {DepositFlowStream} events. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.DepositFlowStream({ streamId: defaultStreamIds[0], funder: users.sender, amount: DEPOSIT_AMOUNT_6D }); - - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[0] }); - - // Second stream to deposit. - vm.expectEmit({ emitter: address(usdc) }); - emit IERC20.Transfer({ from: users.sender, to: address(flow), value: DEPOSIT_AMOUNT_6D }); - vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.DepositFlowStream({ streamId: defaultStreamIds[1], @@ -206,9 +105,6 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { amount: DEPOSIT_AMOUNT_6D }); - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[1] }); - // It should perform the ERC-20 transfers. expectCallToTransferFrom({ token: usdc, from: users.sender, to: address(flow), amount: DEPOSIT_AMOUNT_6D }); expectCallToTransferFrom({ token: usdc, from: users.sender, to: address(flow), amount: DEPOSIT_AMOUNT_6D }); @@ -222,40 +118,26 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { //////////////////////////////////////////////////////////////////////////*/ function test_Batch_Pause() external { - // The calls declared as bytes. bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(flow.pause, (defaultStreamIds[0])); calls[1] = abi.encodeCall(flow.pause, (defaultStreamIds[1])); - uint256 previousTotalDebt0 = flow.totalDebtOf(defaultStreamId); - uint256 previousTotalDebt1 = flow.totalDebtOf(defaultStreamIds[1]); - - // It should emit 2 {PauseFlowStream} and 2 {MetadataUpdate} events. - - // First stream pause. + // It should emit 2 {PauseFlowStream} events. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.PauseFlowStream({ streamId: defaultStreamIds[0], recipient: users.recipient, sender: users.sender, - totalDebt: previousTotalDebt0 + totalDebt: ONE_MONTH_DEBT_6D }); - - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[0] }); - - // Second stream pause. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.PauseFlowStream({ streamId: defaultStreamIds[1], sender: users.sender, recipient: users.recipient, - totalDebt: previousTotalDebt1 + totalDebt: 0 }); - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[1] }); - // Call the batch function. flow.batch(calls); } @@ -273,23 +155,13 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { calls[0] = abi.encodeCall(flow.refund, (defaultStreamIds[0], REFUND_AMOUNT_6D)); calls[1] = abi.encodeCall(flow.refund, (defaultStreamIds[1], REFUND_AMOUNT_6D)); - // It should emit 2 {Transfer} and 2 {RefundFromFlowStream} events. - - // First stream refund. - vm.expectEmit({ emitter: address(usdc) }); - emit IERC20.Transfer({ from: address(flow), to: users.sender, value: REFUND_AMOUNT_6D }); - + // It should emit 2 {RefundFromFlowStream} events. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.RefundFromFlowStream({ streamId: defaultStreamIds[0], sender: users.sender, amount: REFUND_AMOUNT_6D }); - - // Second stream refund. - vm.expectEmit({ emitter: address(usdc) }); - emit IERC20.Transfer({ from: address(flow), to: users.sender, value: REFUND_AMOUNT_6D }); - vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.RefundFromFlowStream({ streamId: defaultStreamIds[1], @@ -318,20 +190,13 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { calls[0] = abi.encodeCall(flow.restart, (defaultStreamIds[0], RATE_PER_SECOND)); calls[1] = abi.encodeCall(flow.restart, (defaultStreamIds[1], RATE_PER_SECOND)); - // It should emit 2 {RestartFlowStream} and 2 {MetadataUpdate} events. - - // First stream restart. + // It should emit 2 {RestartFlowStream} events. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.RestartFlowStream({ streamId: defaultStreamIds[0], sender: users.sender, ratePerSecond: RATE_PER_SECOND }); - - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[0] }); - - // Second stream restart. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.RestartFlowStream({ streamId: defaultStreamIds[1], @@ -339,9 +204,6 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { ratePerSecond: RATE_PER_SECOND }); - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[1] }); - // Call the batch function. flow.batch(calls); } @@ -351,6 +213,9 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { //////////////////////////////////////////////////////////////////////////*/ function test_Batch_Withdraw() external { + // Warp to one more month so that the second stream has also accrued some debt. + vm.warp({ newTimestamp: getBlockTimestamp() + ONE_MONTH }); + depositDefaultAmount(defaultStreamIds[0]); depositDefaultAmount(defaultStreamIds[1]); @@ -359,12 +224,7 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { calls[0] = abi.encodeCall(flow.withdraw, (defaultStreamIds[0], users.recipient, WITHDRAW_AMOUNT_6D)); calls[1] = abi.encodeCall(flow.withdraw, (defaultStreamIds[1], users.recipient, WITHDRAW_AMOUNT_6D)); - // It should emit 2 {Transfer}, 2 {WithdrawFromFlowStream} and 2 {MetadataUpdated} events. - - // First stream withdrawal. - vm.expectEmit({ emitter: address(usdc) }); - emit IERC20.Transfer({ from: address(flow), to: users.recipient, value: WITHDRAW_AMOUNT_6D }); - + // It should emit 2 {WithdrawFromFlowStream} events. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.WithdrawFromFlowStream({ streamId: defaultStreamIds[0], @@ -374,14 +234,6 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { protocolFeeAmount: 0, withdrawAmount: WITHDRAW_AMOUNT_6D }); - - vm.expectEmit({ emitter: address(flow) }); - emit IERC4906.MetadataUpdate({ _tokenId: defaultStreamIds[0] }); - - // Second stream withdrawal. - vm.expectEmit({ emitter: address(usdc) }); - emit IERC20.Transfer({ from: address(flow), to: users.recipient, value: WITHDRAW_AMOUNT_6D }); - vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.WithdrawFromFlowStream({ streamId: defaultStreamIds[1], @@ -400,6 +252,13 @@ contract Batch_Integration_Concrete_Test is Shared_Integration_Concrete_Test { expectCallToTransfer({ token: usdc, to: users.recipient, amount: WITHDRAW_AMOUNT_6D }); // Call the batch function. - flow.batch(calls); + bytes[] memory results = flow.batch(calls); + assertEq(results.length, 2, "batch results length"); + (uint128 actualWithdrawnAmount, uint128 actualProtocolFeeAmount) = abi.decode(results[0], (uint128, uint128)); + assertEq(actualWithdrawnAmount, WITHDRAW_AMOUNT_6D, "batch results[0]"); + assertEq(actualProtocolFeeAmount, 0, "batch results[0]"); + (actualWithdrawnAmount, actualProtocolFeeAmount) = abi.decode(results[1], (uint128, uint128)); + assertEq(actualWithdrawnAmount, WITHDRAW_AMOUNT_6D, "batch results[1]"); + assertEq(actualProtocolFeeAmount, 0, "batch results[1]"); } }