diff --git a/src/SablierV2OpenEnded.sol b/src/SablierV2OpenEnded.sol index 508cae7b..81afd8dd 100644 --- a/src/SablierV2OpenEnded.sol +++ b/src/SablierV2OpenEnded.sol @@ -179,7 +179,7 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope uint256 sendersCount = senders.length; uint256 ratesPerSecondCount = ratesPerSecond.length; - // Check: count of `senders`, `recipients`, `ratesPerSecond` matches. + // Check: count of `senders`, `recipients` and `ratesPerSecond` matches. if (recipientsCount != sendersCount || recipientsCount != ratesPerSecondCount) { revert Errors.SablierV2OpenEnded_CreateMultipleArrayCountsNotEqual( recipientsCount, sendersCount, ratesPerSecondCount diff --git a/test/integration/create-and-deposit-multiple/createAndDepositMultiple.t.sol b/test/integration/create-and-deposit-multiple/createAndDepositMultiple.t.sol new file mode 100644 index 00000000..7c473d30 --- /dev/null +++ b/test/integration/create-and-deposit-multiple/createAndDepositMultiple.t.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22; + +import { ISablierV2OpenEnded } from "src/interfaces/ISablierV2OpenEnded.sol"; +import { Errors } from "src/libraries/Errors.sol"; +import { OpenEnded } from "src/types/DataTypes.sol"; + +import { Integration_Test } from "../Integration.t.sol"; + +contract CreateMultiple_Integration_Test is Integration_Test { + address[] internal defaultRecipients; + address[] internal defaultSenders; + uint128[] internal defaultRatesPerSecond; + uint128[] internal defaultDepositAmounts; + + function setUp() public override { + Integration_Test.setUp(); + + defaultRecipients.push(users.recipient); + defaultRecipients.push(users.recipient); + defaultSenders.push(users.sender); + defaultSenders.push(users.sender); + defaultRatesPerSecond.push(RATE_PER_SECOND); + defaultRatesPerSecond.push(RATE_PER_SECOND); + defaultDepositAmounts.push(DEPOSIT_AMOUNT); + defaultDepositAmounts.push(DEPOSIT_AMOUNT); + } + + function test_RevertWhen_DelegateCall() external { + bytes memory callData = abi.encodeCall( + ISablierV2OpenEnded.createAndDepositMultiple, + (defaultRecipients, defaultSenders, defaultRatesPerSecond, dai, defaultDepositAmounts) + ); + _test_RevertWhen_DelegateCall(callData); + } + + function test_RevertWhen_DepositAmountsArrayIsNotEqual() external whenNotDelegateCalled whenArrayCountsNotEqual { + uint128[] memory depositAmounts = new uint128[](0); + + vm.expectRevert( + abi.encodeWithSelector( + Errors.SablierV2OpenEnded_DepositArrayCountsNotEqual.selector, + defaultRecipients.length, + depositAmounts.length + ) + ); + openEnded.createAndDepositMultiple( + defaultRecipients, defaultSenders, defaultRatesPerSecond, dai, depositAmounts + ); + } + + function test_CreateAndDepositMultiple() external whenNotDelegateCalled whenArrayCountsEqual { + uint256 beforeNextStreamId = openEnded.nextStreamId(); + + vm.expectEmit({ emitter: address(openEnded) }); + emit CreateOpenEndedStream({ + streamId: beforeNextStreamId, + sender: users.sender, + recipient: users.recipient, + ratePerSecond: RATE_PER_SECOND, + asset: dai, + lastTimeUpdate: uint40(block.timestamp) + }); + vm.expectEmit({ emitter: address(openEnded) }); + emit CreateOpenEndedStream({ + streamId: beforeNextStreamId + 1, + sender: users.sender, + recipient: users.recipient, + ratePerSecond: RATE_PER_SECOND, + asset: dai, + lastTimeUpdate: uint40(block.timestamp) + }); + + vm.expectEmit({ emitter: address(openEnded) }); + emit DepositOpenEndedStream({ + streamId: beforeNextStreamId, + funder: users.sender, + asset: dai, + depositAmount: DEPOSIT_AMOUNT + }); + + vm.expectEmit({ emitter: address(openEnded) }); + emit DepositOpenEndedStream({ + streamId: beforeNextStreamId + 1, + funder: users.sender, + asset: dai, + depositAmount: DEPOSIT_AMOUNT + }); + + expectCallToTransferFrom({ asset: dai, from: users.sender, to: address(openEnded), amount: DEPOSIT_AMOUNT }); + expectCallToTransferFrom({ asset: dai, from: users.sender, to: address(openEnded), amount: DEPOSIT_AMOUNT }); + + uint256[] memory streamIds = openEnded.createAndDepositMultiple( + defaultRecipients, defaultSenders, defaultRatesPerSecond, dai, defaultDepositAmounts + ); + + uint256 afterNextStreamId = openEnded.nextStreamId(); + + assertEq(streamIds[0], beforeNextStreamId, "streamIds[0] != beforeNextStreamId"); + assertEq(streamIds[1], beforeNextStreamId + 1, "streamIds[1] != beforeNextStreamId + 1"); + + assertEq(streamIds.length, defaultRecipients.length, "streamIds.length != defaultRecipients.length"); + assertEq( + beforeNextStreamId + defaultRecipients.length, + afterNextStreamId, + "afterNextStreamId != beforeNextStreamId + defaultRecipients.length" + ); + + OpenEnded.Stream memory expectedStream = OpenEnded.Stream({ + ratePerSecond: RATE_PER_SECOND, + asset: dai, + assetDecimals: 18, + balance: DEPOSIT_AMOUNT, + lastTimeUpdate: uint40(block.timestamp), + isCanceled: false, + isStream: true, + recipient: users.recipient, + sender: users.sender + }); + + OpenEnded.Stream memory actualStream = openEnded.getStream(streamIds[0]); + assertEq(actualStream, expectedStream); + + actualStream = openEnded.getStream(streamIds[1]); + assertEq(actualStream, expectedStream); + } +} diff --git a/test/integration/create-and-deposit-multiple/createAndDepositMultiple.tree b/test/integration/create-and-deposit-multiple/createAndDepositMultiple.tree new file mode 100644 index 00000000..f9679821 --- /dev/null +++ b/test/integration/create-and-deposit-multiple/createAndDepositMultiple.tree @@ -0,0 +1,13 @@ +createMultiple.t.sol +├── when delegate called +│ └── it should revert +└── when not delegate called + ├── when array counts are not equal + │ └── when the deposit amounts array is not equal + │ └── it should revert + └── when array counts are equal + ├── it should create multiple streams + ├── it should update the stream balance + ├── it should perform the ERC-20 transfer + ├── it should bump the next stream id multiple times + └── it should emit multiple {CreateOpenEndedStream}, {Transfer} and {DepositOpenEndedStream} events \ No newline at end of file diff --git a/test/integration/create-multiple/createMultiple.t.sol b/test/integration/create-multiple/createMultiple.t.sol index 6b5eec34..e1927335 100644 --- a/test/integration/create-multiple/createMultiple.t.sol +++ b/test/integration/create-multiple/createMultiple.t.sol @@ -30,10 +30,6 @@ contract CreateMultiple_Integration_Test is Integration_Test { _test_RevertWhen_DelegateCall(callData); } - modifier whenArrayCountsNotEqual() { - _; - } - function test_RevertWhen_RecipientsCountNotEqual() external whenNotDelegateCalled whenArrayCountsNotEqual { address[] memory recipients = new address[](1); @@ -76,10 +72,6 @@ contract CreateMultiple_Integration_Test is Integration_Test { openEnded.createMultiple(defaultRecipients, defaultSenders, ratesPerSecond, dai); } - modifier whenArrayCountsEqual() { - _; - } - function test_CreateMultiple() external whenNotDelegateCalled whenArrayCountsEqual { uint256 beforeNextStreamId = openEnded.nextStreamId(); diff --git a/test/integration/create-multiple/createMultiple.tree b/test/integration/create-multiple/createMultiple.tree index fda1e5ac..4f48b4b3 100644 --- a/test/integration/create-multiple/createMultiple.tree +++ b/test/integration/create-multiple/createMultiple.tree @@ -2,14 +2,14 @@ createMultiple.t.sol ├── when delegate called │ └── it should revert └── when not delegate called - ├── when arrays counts are not equal + ├── when array counts are not equal │ ├── when the recipients array is not equal │ │ └── it should revert │ ├── when the senders array is not equal │ │ └── it should revert │ └── when the rates per second array is not equal │ └── it should revert - └── when arrays counts are equal + └── when array counts are equal ├── it should create multiple streams ├── it should bump the next stream id multiple times └── it should emit multiple {CreateOpenEndedStream} events \ No newline at end of file diff --git a/test/utils/Modifiers.sol b/test/utils/Modifiers.sol index b2e5525c..ba8eac9e 100644 --- a/test/utils/Modifiers.sol +++ b/test/utils/Modifiers.sol @@ -62,6 +62,18 @@ abstract contract Modifiers { _; } + /*////////////////////////////////////////////////////////////////////////// + CREATE-MULTIPLE + //////////////////////////////////////////////////////////////////////////*/ + + modifier whenArrayCountsNotEqual() { + _; + } + + modifier whenArrayCountsEqual() { + _; + } + /*////////////////////////////////////////////////////////////////////////// DEPOSIT //////////////////////////////////////////////////////////////////////////*/