Skip to content

Commit

Permalink
feat: add eth fees in flow functions (#348)
Browse files Browse the repository at this point in the history
* feat: add eth fees in flow functions

test: update tests accordingly

* test: add concrete tests for payable functions

* address feedback

* chore: remove redundant function

Signed-off-by: smol-ninja <[email protected]>

---------

Signed-off-by: smol-ninja <[email protected]>
Co-authored-by: smol-ninja <[email protected]>
  • Loading branch information
andreivladbrg and smol-ninja authored Dec 16, 2024
1 parent 41a093f commit 1df87d7
Show file tree
Hide file tree
Showing 17 changed files with 345 additions and 28 deletions.
15 changes: 15 additions & 0 deletions src/SablierFlow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ contract SablierFlow is
UD21x18 newRatePerSecond
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand Down Expand Up @@ -221,6 +222,7 @@ contract SablierFlow is
bool transferable
)
external
payable
override
noDelegateCall
returns (uint256 streamId)
Expand All @@ -239,6 +241,7 @@ contract SablierFlow is
uint128 amount
)
external
payable
override
noDelegateCall
returns (uint256 streamId)
Expand All @@ -258,6 +261,7 @@ contract SablierFlow is
address recipient
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -277,6 +281,7 @@ contract SablierFlow is
uint128 amount
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -300,6 +305,7 @@ contract SablierFlow is
Broker calldata broker
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -316,6 +322,7 @@ contract SablierFlow is
/// @inheritdoc ISablierFlow
function pause(uint256 streamId)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -333,6 +340,7 @@ contract SablierFlow is
uint128 amount
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -349,6 +357,7 @@ contract SablierFlow is
uint128 amount
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -366,6 +375,7 @@ contract SablierFlow is
/// @inheritdoc ISablierFlow
function refundMax(uint256 streamId)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -384,6 +394,7 @@ contract SablierFlow is
UD21x18 ratePerSecond
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -402,6 +413,7 @@ contract SablierFlow is
uint128 amount
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -419,6 +431,7 @@ contract SablierFlow is
/// @inheritdoc ISablierFlow
function void(uint256 streamId)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -436,6 +449,7 @@ contract SablierFlow is
uint128 amount
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand All @@ -452,6 +466,7 @@ contract SablierFlow is
address to
)
external
payable
override
noDelegateCall
notNull(streamId)
Expand Down
2 changes: 1 addition & 1 deletion src/abstracts/Batch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract contract Batch is IBatch {
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IBatch
function batch(bytes[] calldata calls) external override {
function batch(bytes[] calldata calls) external payable override {
uint256 count = calls.length;

for (uint256 i = 0; i < count; ++i) {
Expand Down
16 changes: 16 additions & 0 deletions src/abstracts/SablierFlowBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,22 @@ abstract contract SablierFlowBase is
USER-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc ISablierFlowBase
function collectFees() external override {
uint256 feeAmount = address(this).balance;

// Effect: transfer the fees to the admin.
(bool success,) = admin.call{ value: feeAmount }("");

// Revert if the call failed.
if (!success) {
revert Errors.SablierFlowBase_FeeTransferFail(admin, feeAmount);
}

// Log the fee withdrawal.
emit ISablierFlowBase.CollectFees({ admin: admin, feeAmount: feeAmount });
}

/// @inheritdoc ISablierFlowBase
function collectProtocolRevenue(IERC20 token, address to) external override onlyAdmin {
uint128 revenue = protocolRevenue[token];
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ pragma solidity >=0.8.22;
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;
function batch(bytes[] calldata calls) external payable;
}
27 changes: 16 additions & 11 deletions src/interfaces/ISablierFlow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ interface ISablierFlow is
/// @param streamId The ID of the stream to adjust.
/// @param newRatePerSecond The new rate per second, denoted as a fixed-point number where 1e18 is 1 token
/// per second.
function adjustRatePerSecond(uint256 streamId, UD21x18 newRatePerSecond) external;
function adjustRatePerSecond(uint256 streamId, UD21x18 newRatePerSecond) external payable;

/// @notice Creates a new Flow stream by setting the snapshot time to `block.timestamp` and leaving the balance to
/// zero. The stream is wrapped in an ERC-721 NFT.
Expand Down Expand Up @@ -208,6 +208,7 @@ interface ISablierFlow is
bool transferable
)
external
payable
returns (uint256 streamId);

/// @notice Creates a new Flow stream by setting the snapshot time to `block.timestamp` and the balance to `amount`.
Expand Down Expand Up @@ -239,6 +240,7 @@ interface ISablierFlow is
uint128 amount
)
external
payable
returns (uint256 streamId);

/// @notice Makes a deposit in a stream.
Expand All @@ -255,7 +257,7 @@ interface ISablierFlow is
/// @param amount The deposit amount, denoted in token's decimals.
/// @param sender The stream's sender address.
/// @param recipient The stream's recipient address.
function deposit(uint256 streamId, uint128 amount, address sender, address recipient) external;
function deposit(uint256 streamId, uint128 amount, address sender, address recipient) external payable;

/// @notice Deposits tokens in a stream and pauses it.
///
Expand All @@ -269,7 +271,7 @@ interface ISablierFlow is
///
/// @param streamId The ID of the stream to deposit to, and then pause.
/// @param amount The deposit amount, denoted in token's decimals.
function depositAndPause(uint256 streamId, uint128 amount) external;
function depositAndPause(uint256 streamId, uint128 amount) external payable;

/// @notice Deposits tokens in a stream.
///
Expand Down Expand Up @@ -298,7 +300,8 @@ interface ISablierFlow is
address recipient,
Broker calldata broker
)
external;
external
payable;

/// @notice Pauses the stream.
///
Expand All @@ -314,7 +317,7 @@ interface ISablierFlow is
/// - `msg.sender` must be the stream's sender.
///
/// @param streamId The ID of the stream to pause.
function pause(uint256 streamId) external;
function pause(uint256 streamId) external payable;

/// @notice Refunds the provided amount of tokens from the stream to the sender's address.
///
Expand All @@ -328,7 +331,7 @@ interface ISablierFlow is
///
/// @param streamId The ID of the stream to refund from.
/// @param amount The amount to refund, denoted in token's decimals.
function refund(uint256 streamId, uint128 amount) external;
function refund(uint256 streamId, uint128 amount) external payable;

/// @notice Refunds the provided amount of tokens from the stream to the sender's address.
///
Expand All @@ -342,7 +345,7 @@ interface ISablierFlow is
///
/// @param streamId The ID of the stream to refund from and then pause.
/// @param amount The amount to refund, denoted in token's decimals.
function refundAndPause(uint256 streamId, uint128 amount) external;
function refundAndPause(uint256 streamId, uint128 amount) external payable;

/// @notice Refunds the entire refundable amount of tokens from the stream to the sender's address.
///
Expand All @@ -352,7 +355,7 @@ interface ISablierFlow is
/// - Refer to the requirements in {refund}.
///
/// @param streamId The ID of the stream to refund from.
function refundMax(uint256 streamId) external;
function refundMax(uint256 streamId) external payable;

/// @notice Restarts the stream with the provided rate per second.
///
Expand All @@ -370,7 +373,7 @@ interface ISablierFlow is
/// @param streamId The ID of the stream to restart.
/// @param ratePerSecond The amount by which the debt is increasing every second, denoted as a fixed-point number
/// where 1e18 is 1 token per second.
function restart(uint256 streamId, UD21x18 ratePerSecond) external;
function restart(uint256 streamId, UD21x18 ratePerSecond) external payable;

/// @notice Restarts the stream with the provided rate per second, and makes a deposit.
///
Expand All @@ -387,7 +390,7 @@ interface ISablierFlow is
/// @param ratePerSecond The amount by which the debt is increasing every second, denoted as a fixed-point number
/// where 1e18 is 1 token per second.
/// @param amount The deposit amount, denoted in token's decimals.
function restartAndDeposit(uint256 streamId, UD21x18 ratePerSecond, uint128 amount) external;
function restartAndDeposit(uint256 streamId, UD21x18 ratePerSecond, uint128 amount) external payable;

/// @notice Voids a stream.
///
Expand All @@ -407,7 +410,7 @@ interface ISablierFlow is
/// - `msg.sender` must either be the stream's sender, recipient or an approved third party.
///
/// @param streamId The ID of the stream to void.
function void(uint256 streamId) external;
function void(uint256 streamId) external payable;

/// @notice Withdraws the provided `amount` minus the protocol fee to the provided `to` address.
///
Expand Down Expand Up @@ -436,6 +439,7 @@ interface ISablierFlow is
uint128 amount
)
external
payable
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount);

/// @notice Withdraws the entire withdrawable amount minus the protocol fee to the provided `to` address.
Expand All @@ -458,5 +462,6 @@ interface ISablierFlow is
address to
)
external
payable
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount);
}
13 changes: 13 additions & 0 deletions src/interfaces/ISablierFlowBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ interface ISablierFlowBase is
IERC721Metadata, // 2 inherited components
IAdminable // 0 inherited components
{
/// @notice Emitted when the accrued fees are collected.
/// @param admin The address of the current contract admin, which has received the fees.
/// @param feeAmount The amount of collected fees.
event CollectFees(address indexed admin, uint256 indexed feeAmount);

/// @notice Emitted when the contract admin collects protocol revenue accrued.
/// @param admin The address of the contract admin.
/// @param token The address of the ERC-20 token the protocol revenue has been collected for.
Expand Down Expand Up @@ -145,6 +150,14 @@ interface ISablierFlowBase is
NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Collects the accrued fees by transferring them to the contract admin.
///
/// @dev Emits a {CollectFees} event.
///
/// Notes:
/// - If the admin is a contract, it must be able to receive ETH.
function collectFees() external;

/// @notice Collect the protocol revenue accrued for the provided ERC-20 token.
///
/// @dev Emits {CollectProtocolRevenue} event.
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ library Errors {
SABLIER-FLOW-BASE
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the fee transfer fails.
error SablierFlowBase_FeeTransferFail(address admin, uint256 feeAmount);

/// @notice Thrown when trying to claim protocol revenue when the accrued amount is zero.
error SablierFlowBase_NoProtocolRevenue(address token);

Expand Down
14 changes: 11 additions & 3 deletions tests/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ISablierFlow } from "src/interfaces/ISablierFlow.sol";
import { SablierFlow } from "src/SablierFlow.sol";
import { ERC20MissingReturn } from "./mocks/ERC20MissingReturn.sol";
import { ERC20Mock } from "./mocks/ERC20Mock.sol";
import { ContractWithoutReceive, ContractWithReceive } from "./mocks/Receive.sol";
import { Assertions } from "./utils/Assertions.sol";
import { Modifiers } from "./utils/Modifiers.sol";
import { Users } from "./utils/Types.sol";
Expand All @@ -25,9 +26,11 @@ abstract contract Base_Test is Assertions, Modifiers, Test {
TEST CONTRACTS
//////////////////////////////////////////////////////////////////////////*/

ContractWithoutReceive internal contractWithoutReceive;
ContractWithReceive internal contractWithReceive;
ERC20Mock internal dai;
ERC20Mock internal tokenWithoutDecimals;
ERC20Mock internal tokenWithProtocolFee;
ERC20Mock internal dai;
ERC20Mock internal usdc;
ERC20MissingReturn internal usdt;

Expand All @@ -48,8 +51,13 @@ abstract contract Base_Test is Assertions, Modifiers, Test {
flow = deployOptimizedSablierFlow();
}

// Label the flow contract.
vm.label(address(flow), "Flow");
contractWithoutReceive = new ContractWithoutReceive();
contractWithReceive = new ContractWithReceive();

// Label the contracts.
vm.label({ account: address(flow), newLabel: "Flow" });
vm.label({ account: address(contractWithoutReceive), newLabel: "Contract without Receive" });
vm.label({ account: address(contractWithReceive), newLabel: "Contract with Receive" });

// Create new tokens and label them.
createAndLabelTokens();
Expand Down
Loading

0 comments on commit 1df87d7

Please sign in to comment.