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 eth fees in flow functions #348

Merged
merged 4 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading