Skip to content

Commit 4794aa4

Browse files
committed
feat: add eth fees in flow functions
test: update tests accordingly
1 parent 5fc3f5b commit 4794aa4

File tree

15 files changed

+224
-28
lines changed

15 files changed

+224
-28
lines changed

src/SablierFlow.sol

+15
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ contract SablierFlow is
191191
UD21x18 newRatePerSecond
192192
)
193193
external
194+
payable
194195
override
195196
noDelegateCall
196197
notNull(streamId)
@@ -221,6 +222,7 @@ contract SablierFlow is
221222
bool transferable
222223
)
223224
external
225+
payable
224226
override
225227
noDelegateCall
226228
returns (uint256 streamId)
@@ -239,6 +241,7 @@ contract SablierFlow is
239241
uint128 amount
240242
)
241243
external
244+
payable
242245
override
243246
noDelegateCall
244247
returns (uint256 streamId)
@@ -258,6 +261,7 @@ contract SablierFlow is
258261
address recipient
259262
)
260263
external
264+
payable
261265
override
262266
noDelegateCall
263267
notNull(streamId)
@@ -277,6 +281,7 @@ contract SablierFlow is
277281
uint128 amount
278282
)
279283
external
284+
payable
280285
override
281286
noDelegateCall
282287
notNull(streamId)
@@ -300,6 +305,7 @@ contract SablierFlow is
300305
Broker calldata broker
301306
)
302307
external
308+
payable
303309
override
304310
noDelegateCall
305311
notNull(streamId)
@@ -316,6 +322,7 @@ contract SablierFlow is
316322
/// @inheritdoc ISablierFlow
317323
function pause(uint256 streamId)
318324
external
325+
payable
319326
override
320327
noDelegateCall
321328
notNull(streamId)
@@ -333,6 +340,7 @@ contract SablierFlow is
333340
uint128 amount
334341
)
335342
external
343+
payable
336344
override
337345
noDelegateCall
338346
notNull(streamId)
@@ -349,6 +357,7 @@ contract SablierFlow is
349357
uint128 amount
350358
)
351359
external
360+
payable
352361
override
353362
noDelegateCall
354363
notNull(streamId)
@@ -366,6 +375,7 @@ contract SablierFlow is
366375
/// @inheritdoc ISablierFlow
367376
function refundMax(uint256 streamId)
368377
external
378+
payable
369379
override
370380
noDelegateCall
371381
notNull(streamId)
@@ -384,6 +394,7 @@ contract SablierFlow is
384394
UD21x18 ratePerSecond
385395
)
386396
external
397+
payable
387398
override
388399
noDelegateCall
389400
notNull(streamId)
@@ -402,6 +413,7 @@ contract SablierFlow is
402413
uint128 amount
403414
)
404415
external
416+
payable
405417
override
406418
noDelegateCall
407419
notNull(streamId)
@@ -419,6 +431,7 @@ contract SablierFlow is
419431
/// @inheritdoc ISablierFlow
420432
function void(uint256 streamId)
421433
external
434+
payable
422435
override
423436
noDelegateCall
424437
notNull(streamId)
@@ -436,6 +449,7 @@ contract SablierFlow is
436449
uint128 amount
437450
)
438451
external
452+
payable
439453
override
440454
noDelegateCall
441455
notNull(streamId)
@@ -452,6 +466,7 @@ contract SablierFlow is
452466
address to
453467
)
454468
external
469+
payable
455470
override
456471
noDelegateCall
457472
notNull(streamId)

src/abstracts/Batch.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ abstract contract Batch is IBatch {
1313
//////////////////////////////////////////////////////////////////////////*/
1414

1515
/// @inheritdoc IBatch
16-
function batch(bytes[] calldata calls) external override {
16+
function batch(bytes[] calldata calls) external payable override {
1717
uint256 count = calls.length;
1818

1919
for (uint256 i = 0; i < count; ++i) {

src/abstracts/SablierFlowBase.sol

+16
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,22 @@ abstract contract SablierFlowBase is
205205
USER-FACING NON-CONSTANT FUNCTIONS
206206
//////////////////////////////////////////////////////////////////////////*/
207207

208+
/// @inheritdoc ISablierFlowBase
209+
function collectFees() external override {
210+
uint256 feeAmount = address(this).balance;
211+
212+
// Effect: transfer the fees to the admin.
213+
(bool success,) = admin.call{ value: feeAmount }("");
214+
215+
// Revert if the call failed.
216+
if (!success) {
217+
revert Errors.SablierFlowBase_FeeTransferFail(admin, feeAmount);
218+
}
219+
220+
// Log the fee withdrawal.
221+
emit ISablierFlowBase.CollectFees({ admin: admin, feeAmount: feeAmount });
222+
}
223+
208224
/// @inheritdoc ISablierFlowBase
209225
function collectProtocolRevenue(IERC20 token, address to) external override onlyAdmin {
210226
uint128 revenue = protocolRevenue[token];

src/interfaces/IBatch.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ pragma solidity >=0.8.22;
55
interface IBatch {
66
/// @notice Allows batched call to self, `this` contract.
77
/// @param calls An array of inputs for each call.
8-
function batch(bytes[] calldata calls) external;
8+
function batch(bytes[] calldata calls) external payable;
99
}

src/interfaces/ISablierFlow.sol

+16-11
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ interface ISablierFlow is
178178
/// @param streamId The ID of the stream to adjust.
179179
/// @param newRatePerSecond The new rate per second, denoted as a fixed-point number where 1e18 is 1 token
180180
/// per second.
181-
function adjustRatePerSecond(uint256 streamId, UD21x18 newRatePerSecond) external;
181+
function adjustRatePerSecond(uint256 streamId, UD21x18 newRatePerSecond) external payable;
182182

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

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

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

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

274276
/// @notice Deposits tokens in a stream.
275277
///
@@ -298,7 +300,8 @@ interface ISablierFlow is
298300
address recipient,
299301
Broker calldata broker
300302
)
301-
external;
303+
external
304+
payable;
302305

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

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

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

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

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

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

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

412415
/// @notice Withdraws the provided `amount` minus the protocol fee to the provided `to` address.
413416
///
@@ -436,6 +439,7 @@ interface ISablierFlow is
436439
uint128 amount
437440
)
438441
external
442+
payable
439443
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount);
440444

441445
/// @notice Withdraws the entire withdrawable amount minus the protocol fee to the provided `to` address.
@@ -458,5 +462,6 @@ interface ISablierFlow is
458462
address to
459463
)
460464
external
465+
payable
461466
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount);
462467
}

src/interfaces/ISablierFlowBase.sol

+13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ interface ISablierFlowBase is
1919
IERC721Metadata, // 2 inherited components
2020
IAdminable // 0 inherited components
2121
{
22+
/// @notice Emitted when the accrued fees are collected.
23+
/// @param admin The address of the current contract admin, which has received the fees.
24+
/// @param feeAmount The amount of collected fees.
25+
event CollectFees(address indexed admin, uint256 indexed feeAmount);
26+
2227
/// @notice Emitted when the contract admin collects protocol revenue accrued.
2328
/// @param admin The address of the contract admin.
2429
/// @param token The address of the ERC-20 token the protocol revenue has been collected for.
@@ -145,6 +150,14 @@ interface ISablierFlowBase is
145150
NON-CONSTANT FUNCTIONS
146151
//////////////////////////////////////////////////////////////////////////*/
147152

153+
/// @notice Collects the accrued fees by transferring them to the contract admin.
154+
///
155+
/// @dev Emits a {CollectFees} event.
156+
///
157+
/// Notes:
158+
/// - If the admin is a contract, it must be able to receive ETH.
159+
function collectFees() external;
160+
148161
/// @notice Collect the protocol revenue accrued for the provided ERC-20 token.
149162
///
150163
/// @dev Emits {CollectProtocolRevenue} event.

src/libraries/Errors.sol

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ library Errors {
9191
SABLIER-FLOW-BASE
9292
//////////////////////////////////////////////////////////////////////////*/
9393

94+
/// @notice Thrown when the fee transfer fails.
95+
error SablierFlowBase_FeeTransferFail(address admin, uint256 feeAmount);
96+
9497
/// @notice Thrown when trying to claim protocol revenue when the accrued amount is zero.
9598
error SablierFlowBase_NoProtocolRevenue(address token);
9699

tests/Base.t.sol

+11-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ISablierFlow } from "src/interfaces/ISablierFlow.sol";
88
import { SablierFlow } from "src/SablierFlow.sol";
99
import { ERC20MissingReturn } from "./mocks/ERC20MissingReturn.sol";
1010
import { ERC20Mock } from "./mocks/ERC20Mock.sol";
11+
import { ContractWithoutReceive, ContractWithReceive } from "./mocks/Receive.sol";
1112
import { Assertions } from "./utils/Assertions.sol";
1213
import { Modifiers } from "./utils/Modifiers.sol";
1314
import { Users } from "./utils/Types.sol";
@@ -25,9 +26,11 @@ abstract contract Base_Test is Assertions, Modifiers, Test {
2526
TEST CONTRACTS
2627
//////////////////////////////////////////////////////////////////////////*/
2728

29+
ContractWithoutReceive internal contractWithoutReceive;
30+
ContractWithReceive internal contractWithReceive;
31+
ERC20Mock internal dai;
2832
ERC20Mock internal tokenWithoutDecimals;
2933
ERC20Mock internal tokenWithProtocolFee;
30-
ERC20Mock internal dai;
3134
ERC20Mock internal usdc;
3235
ERC20MissingReturn internal usdt;
3336

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

51-
// Label the flow contract.
52-
vm.label(address(flow), "Flow");
54+
contractWithoutReceive = new ContractWithoutReceive();
55+
contractWithReceive = new ContractWithReceive();
56+
57+
// Label the contracts.
58+
vm.label({ account: address(flow), newLabel: "Flow" });
59+
vm.label({ account: address(contractWithoutReceive), newLabel: "Contract without Receive" });
60+
vm.label({ account: address(contractWithReceive), newLabel: "Contract with Receive" });
5361

5462
// Create new tokens and label them.
5563
createAndLabelTokens();

0 commit comments

Comments
 (0)