Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add batch functions #49

Merged
merged 21 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9cfe2ff
feat: add withdrawMultiple function
andreivladbrg Apr 15, 2024
cb8fe0a
chore: capitalize ID in comments
andreivladbrg Apr 15, 2024
ea43360
test: rename function to expectRevertNull
andreivladbrg Apr 18, 2024
7683de2
test: cancelMultiple function
andreivladbrg Apr 18, 2024
14f1024
test: add defaultStreamIds array in Integration_Test
andreivladbrg Apr 19, 2024
8e52416
test: withdrawMultiple function
andreivladbrg Apr 19, 2024
b4d9d47
feat: implement createMultiple function
andreivladbrg Apr 20, 2024
34ed95c
feat: implement createAndDepositMultiple function
andreivladbrg Apr 22, 2024
53f7b3a
docs: improve readability for streamId requirements
andreivladbrg Apr 22, 2024
0d0576a
refactor: change order of array counts in custom error
andreivladbrg Apr 22, 2024
6cca0e6
test: createAndDepositMultiple function
andreivladbrg Apr 24, 2024
e15c02c
Refactoring open ended (#52)
smol-ninja Apr 29, 2024
6d3c8ac
test: refactor streamDebtOf
smol-ninja Apr 29, 2024
a5c38d8
test: remove unneeded console log
andreivladbrg Apr 29, 2024
b8b2130
refactor: say "amount" in function paramaters instead of explicit names
andreivladbrg Apr 29, 2024
7dd64ce
test: correct the contract name
andreivladbrg Apr 29, 2024
1e120e7
perf: optimize createAndDepositMultiple
andreivladbrg Apr 29, 2024
cfa028a
test: remove unneeded delegatecall test in cancelMultiple
andreivladbrg Apr 29, 2024
3569fed
test: refactoring relates changes
smol-ninja May 1, 2024
043149f
test: remove unneeded tree branches
andreivladbrg May 7, 2024
183181e
test: add modifiers in test_CancelMultiple
andreivladbrg May 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion script/Base.s.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// solhint-disable no-console
pragma solidity >=0.8.22;

import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
Expand Down Expand Up @@ -57,7 +58,7 @@ abstract contract BaseScript is Script {
string memory json = vm.readFile("package.json");
string memory version = json.readString(".version");
string memory create2Salt = string.concat("ChainID ", chainId, ", Version ", version);
console2.log("The CREATE2 salt is \"%s\"", create2Salt);
console2.log("The CREATE2 salt is %s", create2Salt);
return bytes32(abi.encodePacked(create2Salt));
}
}
183 changes: 156 additions & 27 deletions src/SablierV2OpenEnded.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
view
override
notCanceled(streamId)
notNull(streamId)
returns (uint128 refundableAmount)
{
refundableAmount = _refundableAmountOf(streamId, uint40(block.timestamp));
Expand All @@ -42,25 +43,40 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
view
override
notCanceled(streamId)
notNull(streamId)
returns (uint128 refundableAmount)
{
refundableAmount = _refundableAmountOf(streamId, time);
}

/// @inheritdoc ISablierV2OpenEnded
function streamDebt(uint256 streamId) external view notCanceled(streamId) returns (uint128 debt) {
function streamDebtOf(uint256 streamId)
external
view
override
notCanceled(streamId)
notNull(streamId)
returns (uint128 debt)
{
uint128 balance = _streams[streamId].balance;
uint128 streamedAmount = _streamedAmountOf(streamId, uint40(block.timestamp));

if (balance >= streamedAmount) {
if (balance < streamedAmount) {
debt = streamedAmount - balance;
} else {
smol-ninja marked this conversation as resolved.
Show resolved Hide resolved
return 0;
}

debt = streamedAmount - balance;
}

/// @inheritdoc ISablierV2OpenEnded
function streamedAmountOf(uint256 streamId) external view notCanceled(streamId) returns (uint128 streamedAmount) {
function streamedAmountOf(uint256 streamId)
external
view
override
notCanceled(streamId)
notNull(streamId)
returns (uint128 streamedAmount)
{
streamedAmount = _streamedAmountOf(streamId, uint40(block.timestamp));
}

Expand All @@ -71,7 +87,9 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
)
external
view
override
notCanceled(streamId)
notNull(streamId)
returns (uint128 streamedAmount)
{
streamedAmount = _streamedAmountOf(streamId, time);
Expand All @@ -81,7 +99,9 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
function withdrawableAmountOf(uint256 streamId)
external
view
override
notCanceled(streamId)
notNull(streamId)
returns (uint128 withdrawableAmount)
{
withdrawableAmount = _withdrawableAmountOf(streamId, uint40(block.timestamp));
Expand All @@ -94,7 +114,9 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
)
external
view
override
notCanceled(streamId)
notNull(streamId)
returns (uint128 withdrawableAmount)
{
withdrawableAmount = _withdrawableAmountOf(streamId, time);
Expand All @@ -110,19 +132,38 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
uint128 newRatePerSecond
)
external
override
noDelegateCall
notCanceled(streamId)
notNull(streamId)
onlySender(streamId)
{
// Effects and Interactions: adjust the stream.
_adjustRatePerSecond(streamId, newRatePerSecond);
}

/// @inheritdoc ISablierV2OpenEnded
function cancel(uint256 streamId) external noDelegateCall notCanceled(streamId) onlySender(streamId) {
function cancel(uint256 streamId)
public
override
noDelegateCall
notCanceled(streamId)
notNull(streamId)
onlySender(streamId)
{
_cancel(streamId);
}

/// @inheritdoc ISablierV2OpenEnded
function cancelMultiple(uint256[] calldata streamIds) external override {
smol-ninja marked this conversation as resolved.
Show resolved Hide resolved
// Iterate over the provided array of stream IDs and cancel each stream.
uint256 count = streamIds.length;
for (uint256 i = 0; i < count; ++i) {
// Effects and Interactions: cancel the stream.
cancel(streamIds[i]);
}
}

/// @inheritdoc ISablierV2OpenEnded
function create(
address sender,
Expand All @@ -131,6 +172,7 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
IERC20 asset
)
external
override
returns (uint256 streamId)
{
// Checks, Effects and Interactions: create the stream.
Expand All @@ -143,26 +185,92 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
address recipient,
uint128 ratePerSecond,
IERC20 asset,
uint128 depositAmount
uint128 amount
)
external
override
returns (uint256 streamId)
{
// Checks, Effects and Interactions: create the stream.
streamId = _create(sender, recipient, ratePerSecond, asset);

// Checks, Effects and Interactions: deposit on stream.
_deposit(streamId, depositAmount);
_deposit(streamId, amount);
}

/// @inheritdoc ISablierV2OpenEnded
function createMultiple(
address[] calldata recipients,
address[] calldata senders,
uint128[] calldata ratesPerSecond,
IERC20 asset
)
public
override
returns (uint256[] memory streamIds)
{
uint256 recipientsCount = recipients.length;
uint256 sendersCount = senders.length;
uint256 ratesPerSecondCount = ratesPerSecond.length;

// Check: count of `senders`, `recipients` and `ratesPerSecond` matches.
if (recipientsCount != sendersCount || recipientsCount != ratesPerSecondCount) {
revert Errors.SablierV2OpenEnded_CreateMultipleArrayCountsNotEqual(
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
recipientsCount, sendersCount, ratesPerSecondCount
);
}

streamIds = new uint256[](recipientsCount);
for (uint256 i = 0; i < recipientsCount; ++i) {
// Checks, Effects and Interactions: create the stream.
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
streamIds[i] = _create(senders[i], recipients[i], ratesPerSecond[i], asset);
}
}

/// @inheritdoc ISablierV2OpenEnded
function createAndDepositMultiple(
address[] calldata recipients,
address[] calldata senders,
uint128[] calldata ratesPerSecond,
IERC20 asset,
uint128[] calldata amounts
)
external
override
returns (uint256[] memory streamIds)
{
streamIds = new uint256[](recipients.length);
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
streamIds = createMultiple(recipients, senders, ratesPerSecond, asset);

uint256 streamIdsCount = streamIds.length;
if (streamIdsCount != amounts.length) {
revert Errors.SablierV2OpenEnded_DepositArrayCountsNotEqual(streamIdsCount, amounts.length);
}

// Deposit on each stream.
for (uint256 i = 0; i < streamIdsCount; ++i) {
// Checks, Effects and Interactions: deposit on stream.
_deposit(streamIds[i], amounts[i]);
}
}

/// @inheritdoc ISablierV2OpenEnded
function deposit(uint256 streamId, uint128 amount) external noDelegateCall notCanceled(streamId) {
function deposit(
uint256 streamId,
uint128 amount
)
external
override
noDelegateCall
notCanceled(streamId)
notNull(streamId)
{
// Checks, Effects and Interactions: deposit on stream.
_deposit(streamId, amount);
}

/// @inheritdoc ISablierV2OpenEnded
function depositMultiple(uint256[] calldata streamIds, uint128[] calldata amounts) external noDelegateCall {
function depositMultiple(uint256[] memory streamIds, uint128[] calldata amounts) public override noDelegateCall {
uint256 streamIdsCount = streamIds.length;
uint256 amountsCount = amounts.length;

Expand All @@ -171,36 +279,30 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
revert Errors.SablierV2OpenEnded_DepositArrayCountsNotEqual(streamIdsCount, amountsCount);
}

uint256 streamId;
uint128 amount;
for (uint256 i = 0; i < streamIdsCount; ++i) {
streamId = streamIds[i];

// Check: the stream is not canceled.
if (isCanceled(streamId)) {
revert Errors.SablierV2OpenEnded_StreamCanceled(streamId);
if (isCanceled(streamIds[i])) {
revert Errors.SablierV2OpenEnded_StreamCanceled(streamIds[i]);
}

amount = amounts[i];

// Checks, Effects and Interactions: deposit on stream.
_deposit(streamId, amount);
_deposit(streamIds[i], amounts[i]);
}
}

/// @inheritdoc ISablierV2OpenEnded
function restartStream(uint256 streamId, uint128 ratePerSecond) external {
function restartStream(uint256 streamId, uint128 ratePerSecond) external override {
// Checks, Effects and Interactions: restart the stream.
_restartStream(streamId, ratePerSecond);
}

/// @inheritdoc ISablierV2OpenEnded
function restartStreamAndDeposit(uint256 streamId, uint128 ratePerSecond, uint128 depositAmount) external {
function restartStreamAndDeposit(uint256 streamId, uint128 ratePerSecond, uint128 amount) external override {
// Checks, Effects and Interactions: restart the stream.
_restartStream(streamId, ratePerSecond);

// Checks, Effects and Interactions: deposit on stream.
_deposit(streamId, depositAmount);
_deposit(streamId, amount);
}

/// @inheritdoc ISablierV2OpenEnded
Expand All @@ -209,22 +311,40 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
uint128 amount
)
external
override
noDelegateCall
notCanceled(streamId)
notNull(streamId)
onlySender(streamId)
{
// Checks, Effects and Interactions: make the refund.
_refundFromStream(streamId, amount);
}

/// @inheritdoc ISablierV2OpenEnded
function withdraw(uint256 streamId, address to, uint40 time) external {
function withdraw(uint256 streamId, address to, uint40 time) external override {
// Checks, Effects and Interactions: make the withdrawal.
_withdraw(streamId, to, time);
}

/// @inheritdoc ISablierV2OpenEnded
function withdrawMax(uint256 streamId, address to) external {
function withdrawMultiple(uint256[] calldata streamIds, uint40[] calldata times) external override noDelegateCall {
// Check: there is an equal number of `streamIds` and `amounts`.
uint256 streamIdsCount = streamIds.length;
uint256 timesCount = times.length;
if (streamIdsCount != timesCount) {
revert Errors.SablierV2OpenEnded_WithdrawMultipleArrayCountsNotEqual(streamIdsCount, timesCount);
smol-ninja marked this conversation as resolved.
Show resolved Hide resolved
}

// Iterate over the provided array of stream IDs, and withdraw from each stream to the recipient.
for (uint256 i = 0; i < streamIdsCount; ++i) {
// Checks, Effects and Interactions: check the parameters and make the withdrawal.
_withdraw({ streamId: streamIds[i], to: _streams[streamIds[i]].recipient, time: times[i] });
}
}

/// @inheritdoc ISablierV2OpenEnded
function withdrawMax(uint256 streamId, address to) external override {
// Checks, Effects and Interactions: make the withdrawal.
_withdraw(streamId, to, uint40(block.timestamp));
}
Expand Down Expand Up @@ -292,7 +412,7 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope

/// @dev Calculates the streamed amount.
function _streamedAmountOf(uint256 streamId, uint40 time) internal view returns (uint128) {
uint128 lastTimeUpdate = uint128(_streams[streamId].lastTimeUpdate);
uint40 lastTimeUpdate = _streams[streamId].lastTimeUpdate;

// If the time reference is less than or equal to the `lastTimeUpdate`, return zero.
if (time <= lastTimeUpdate) {
Expand Down Expand Up @@ -473,7 +593,7 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope

/// @dev See the documentation for the user-facing functions that call this internal function.
function _deposit(uint256 streamId, uint128 amount) internal {
// Check: the amount is not zero.
// Check: the deposit amount is not zero.
if (amount == 0) {
revert Errors.SablierV2OpenEnded_DepositAmountZero();
}
Expand Down Expand Up @@ -567,7 +687,16 @@ contract SablierV2OpenEnded is ISablierV2OpenEnded, NoDelegateCall, SablierV2Ope
}

/// @dev See the documentation for the user-facing functions that call this internal function.
function _withdraw(uint256 streamId, address to, uint40 time) internal noDelegateCall notCanceled(streamId) {
function _withdraw(
uint256 streamId,
address to,
uint40 time
)
internal
noDelegateCall
notCanceled(streamId)
notNull(streamId)
{
// Check: the withdrawal address is not zero.
if (to == address(0)) {
revert Errors.SablierV2OpenEnded_WithdrawToZeroAddress();
Expand Down
Loading