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

Dry fy shared logic and add util Modifiers contract #1058

Merged
merged 11 commits into from
Nov 1, 2024
20 changes: 16 additions & 4 deletions test/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity >=0.8.22 <0.9.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { ILockupNFTDescriptor } from "src/core/interfaces/ILockupNFTDescriptor.sol";
import { ISablierLockupDynamic } from "src/core/interfaces/ISablierLockupDynamic.sol";
import { ISablierLockupLinear } from "src/core/interfaces/ISablierLockupLinear.sol";
Expand All @@ -17,21 +18,20 @@ import { ISablierMerkleLL } from "src/periphery/interfaces/ISablierMerkleLL.sol"
import { ISablierMerkleLT } from "src/periphery/interfaces/ISablierMerkleLT.sol";
import { SablierBatchLockup } from "src/periphery/SablierBatchLockup.sol";
import { SablierMerkleFactory } from "src/periphery/SablierMerkleFactory.sol";

import { ERC20MissingReturn } from "./mocks/erc20/ERC20MissingReturn.sol";
import { ERC20Mock } from "./mocks/erc20/ERC20Mock.sol";
import { RecipientGood } from "./mocks/Hooks.sol";
import { Noop } from "./mocks/Noop.sol";
import { Assertions } from "./utils/Assertions.sol";
import { Calculations } from "./utils/Calculations.sol";
import { Constants } from "./utils/Constants.sol";
import { Defaults } from "./utils/Defaults.sol";
import { DeployOptimized } from "./utils/DeployOptimized.sol";
import { Events } from "./utils/Events.sol";
import { Fuzzers } from "./utils/Fuzzers.sol";
import { Modifiers } from "./utils/Modifiers.sol";
import { Users } from "./utils/Types.sol";

/// @notice Base test contract with common logic needed by all tests.
abstract contract Base_Test is Assertions, Calculations, Constants, DeployOptimized, Events, Fuzzers {
abstract contract Base_Test is Assertions, Calculations, DeployOptimized, Modifiers {
/*//////////////////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -104,6 +104,18 @@ abstract contract Base_Test is Assertions, Calculations, Constants, DeployOptimi
defaults.setUsers(users);
defaults.initMerkleTree();

// Set the variables in Modifiers contract.
setVariables(defaults, users);

// Approve `users.operator` to operate over lockups on behalf of the `users.recipient`.
resetPrank({ msgSender: users.recipient });
lockupDynamic.setApprovalForAll(users.operator, true);
lockupLinear.setApprovalForAll(users.operator, true);
lockupTranched.setApprovalForAll(users.operator, true);

// Set sender as the default caller for the tests.
resetPrank({ msgSender: users.sender });

// Warp to July 1, 2024 at 00:00 UTC to provide a more realistic testing environment.
vm.warp({ newTimestamp: JULY_1_2024 });
}
Expand Down
17 changes: 9 additions & 8 deletions test/core/fork/LockupDynamic.t.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22 <0.9.0;

import { IERC4906 } from "@openzeppelin/contracts/interfaces/IERC4906.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Solarray } from "solarray/src/Solarray.sol";

import { ISablierLockup } from "src/core/interfaces/ISablierLockup.sol";
import { ISablierLockupDynamic } from "src/core/interfaces/ISablierLockupDynamic.sol";
import { Broker, Lockup, LockupDynamic } from "src/core/types/DataTypes.sol";

import { Fork_Test } from "./Fork.t.sol";

abstract contract LockupDynamic_Fork_Test is Fork_Test {
Expand Down Expand Up @@ -145,9 +146,9 @@ abstract contract LockupDynamic_Fork_Test is Fork_Test {

// Expect the relevant events to be emitted.
vm.expectEmit({ emitter: address(lockupDynamic) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });
vm.expectEmit({ emitter: address(lockupDynamic) });
emit CreateLockupDynamicStream({
emit ISablierLockupDynamic.CreateLockupDynamicStream({
streamId: vars.streamId,
funder: FORK_ASSET_HOLDER,
sender: params.sender,
Expand Down Expand Up @@ -268,14 +269,14 @@ abstract contract LockupDynamic_Fork_Test is Fork_Test {

// Expect the relevant events to be emitted.
vm.expectEmit({ emitter: address(lockupDynamic) });
emit WithdrawFromLockupStream({
emit ISablierLockup.WithdrawFromLockupStream({
streamId: vars.streamId,
to: params.recipient,
asset: FORK_ASSET,
amount: params.withdrawAmount
});
vm.expectEmit({ emitter: address(lockupDynamic) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });

// Make the withdrawal.
resetPrank({ msgSender: params.recipient });
Expand Down Expand Up @@ -334,11 +335,11 @@ abstract contract LockupDynamic_Fork_Test is Fork_Test {
vm.expectEmit({ emitter: address(lockupDynamic) });
vars.senderAmount = lockupDynamic.refundableAmountOf(vars.streamId);
vars.recipientAmount = lockupDynamic.withdrawableAmountOf(vars.streamId);
emit CancelLockupStream(
emit ISablierLockup.CancelLockupStream(
vars.streamId, params.sender, params.recipient, FORK_ASSET, vars.senderAmount, vars.recipientAmount
);
vm.expectEmit({ emitter: address(lockupDynamic) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });

// Cancel the stream.
resetPrank({ msgSender: params.sender });
Expand Down
17 changes: 9 additions & 8 deletions test/core/fork/LockupLinear.t.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22 <0.9.0;

import { IERC4906 } from "@openzeppelin/contracts/interfaces/IERC4906.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ud } from "@prb/math/src/UD60x18.sol";
import { Solarray } from "solarray/src/Solarray.sol";

import { ISablierLockup } from "src/core/interfaces/ISablierLockup.sol";
import { ISablierLockupLinear } from "src/core/interfaces/ISablierLockupLinear.sol";
import { Broker, Lockup, LockupLinear } from "src/core/types/DataTypes.sol";

import { Fork_Test } from "./Fork.t.sol";

abstract contract LockupLinear_Fork_Test is Fork_Test {
Expand Down Expand Up @@ -155,9 +156,9 @@ abstract contract LockupLinear_Fork_Test is Fork_Test {

// Expect the relevant events to be emitted.
vm.expectEmit({ emitter: address(lockupLinear) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });
vm.expectEmit({ emitter: address(lockupLinear) });
emit CreateLockupLinearStream({
emit ISablierLockupLinear.CreateLockupLinearStream({
streamId: vars.streamId,
funder: FORK_ASSET_HOLDER,
sender: params.sender,
Expand Down Expand Up @@ -274,14 +275,14 @@ abstract contract LockupLinear_Fork_Test is Fork_Test {

// Expect the relevant events to be emitted.
vm.expectEmit({ emitter: address(lockupLinear) });
emit WithdrawFromLockupStream({
emit ISablierLockup.WithdrawFromLockupStream({
streamId: vars.streamId,
to: params.recipient,
asset: FORK_ASSET,
amount: params.withdrawAmount
});
vm.expectEmit({ emitter: address(lockupLinear) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });

// Make the withdrawal.
resetPrank({ msgSender: params.recipient });
Expand Down Expand Up @@ -338,11 +339,11 @@ abstract contract LockupLinear_Fork_Test is Fork_Test {
vm.expectEmit({ emitter: address(lockupLinear) });
vars.senderAmount = lockupLinear.refundableAmountOf(vars.streamId);
vars.recipientAmount = lockupLinear.withdrawableAmountOf(vars.streamId);
emit CancelLockupStream(
emit ISablierLockup.CancelLockupStream(
vars.streamId, params.sender, params.recipient, FORK_ASSET, vars.senderAmount, vars.recipientAmount
);
vm.expectEmit({ emitter: address(lockupLinear) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });

// Cancel the stream.
resetPrank({ msgSender: params.sender });
Expand Down
17 changes: 9 additions & 8 deletions test/core/fork/LockupTranched.t.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22 <0.9.0;

import { IERC4906 } from "@openzeppelin/contracts/interfaces/IERC4906.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Solarray } from "solarray/src/Solarray.sol";

import { ISablierLockup } from "src/core/interfaces/ISablierLockup.sol";
import { ISablierLockupTranched } from "src/core/interfaces/ISablierLockupTranched.sol";
import { Broker, Lockup, LockupTranched } from "src/core/types/DataTypes.sol";

import { Fork_Test } from "./Fork.t.sol";

abstract contract LockupTranched_Fork_Test is Fork_Test {
Expand Down Expand Up @@ -145,9 +146,9 @@ abstract contract LockupTranched_Fork_Test is Fork_Test {

// Expect the relevant events to be emitted.
vm.expectEmit({ emitter: address(lockupTranched) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });
vm.expectEmit({ emitter: address(lockupTranched) });
emit CreateLockupTranchedStream({
emit ISablierLockupTranched.CreateLockupTranchedStream({
streamId: vars.streamId,
funder: FORK_ASSET_HOLDER,
sender: params.sender,
Expand Down Expand Up @@ -268,14 +269,14 @@ abstract contract LockupTranched_Fork_Test is Fork_Test {

// Expect the relevant events to be emitted.
vm.expectEmit({ emitter: address(lockupTranched) });
emit WithdrawFromLockupStream({
emit ISablierLockup.WithdrawFromLockupStream({
streamId: vars.streamId,
to: params.recipient,
asset: FORK_ASSET,
amount: params.withdrawAmount
});
vm.expectEmit({ emitter: address(lockupTranched) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });

// Make the withdrawal.
resetPrank({ msgSender: params.recipient });
Expand Down Expand Up @@ -334,11 +335,11 @@ abstract contract LockupTranched_Fork_Test is Fork_Test {
vm.expectEmit({ emitter: address(lockupTranched) });
vars.senderAmount = lockupTranched.refundableAmountOf(vars.streamId);
vars.recipientAmount = lockupTranched.withdrawableAmountOf(vars.streamId);
emit CancelLockupStream(
emit ISablierLockup.CancelLockupStream(
vars.streamId, params.sender, params.recipient, FORK_ASSET, vars.senderAmount, vars.recipientAmount
);
vm.expectEmit({ emitter: address(lockupTranched) });
emit MetadataUpdate({ _tokenId: vars.streamId });
emit IERC4906.MetadataUpdate({ _tokenId: vars.streamId });

// Cancel the stream.
resetPrank({ msgSender: params.sender });
Expand Down
71 changes: 63 additions & 8 deletions test/core/integration/Integration.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22 <0.9.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { ISablierLockup } from "src/core/interfaces/ISablierLockup.sol";
import { Errors } from "src/core/libraries/Errors.sol";
import { Broker } from "src/core/types/DataTypes.sol";

import { Base_Test } from "../../Base.t.sol";
import {
Expand All @@ -13,12 +17,22 @@ import {
} from "../../mocks/Hooks.sol";

/// @notice Common logic needed by all integration tests, both concrete and fuzz tests.

abstract contract Integration_Test is Base_Test {
/*//////////////////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////////////////*/

uint256 internal defaultStreamId;
uint256 internal notTransferableStreamId;

/*//////////////////////////////////////////////////////////////////////////
TEST CONTRACTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev A test contract that is meant to be overridden by the implementing contract, which will be
/// either {SablierLockupDynamic}, {SablierLockupLinear} or {SablierLockupTranched}.
ISablierLockup internal lockup;

RecipientInterfaceIDIncorrect internal recipientInterfaceIDIncorrect;
RecipientInterfaceIDMissing internal recipientInterfaceIDMissing;
RecipientInvalidSelector internal recipientInvalidSelector;
Expand All @@ -44,13 +58,6 @@ abstract contract Integration_Test is Base_Test {
vm.label({ account: address(recipientReverting), newLabel: "Recipient Reverting" });
}

/*//////////////////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////////////////*/

/// @dev Labels the most relevant contracts.
function labelContracts() internal { }

/*//////////////////////////////////////////////////////////////////////////
EXPECT CALLS
//////////////////////////////////////////////////////////////////////////*/
Expand All @@ -60,4 +67,52 @@ abstract contract Integration_Test is Base_Test {
assertFalse(success, "delegatecall success");
assertEq(returnData, abi.encodeWithSelector(Errors.DelegateCall.selector), "delegatecall return data");
}

/*//////////////////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////////////////*/

/// @dev Creates the default stream.
function createDefaultStream() internal virtual returns (uint256 streamId);

/// @dev Creates the default stream but make it not cancelable.
function createDefaultStreamNotCancelable() internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the NFT transfer disabled.
function createDefaultStreamNotTransferable() internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided address.
function createDefaultStreamWithAsset(IERC20 asset) internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided broker.
function createDefaultStreamWithBroker(Broker memory broker) internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided end time.
function createDefaultStreamWithEndTime(uint40 endTime) internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided user as the recipient and the sender.
function createDefaultStreamWithIdenticalUsers(address user) internal returns (uint256 streamId) {
return createDefaultStreamWithUsers({ recipient: user, sender: user });
}

/// @dev Creates the default stream with the provided recipient.
function createDefaultStreamWithRecipient(address recipient) internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided sender.
function createDefaultStreamWithSender(address sender) internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided start time.
function createDefaultStreamWithStartTime(uint40 startTime) internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided total amount.
function createDefaultStreamWithTotalAmount(uint128 totalAmount) internal virtual returns (uint256 streamId);

/// @dev Creates the default stream with the provided sender and recipient.
function createDefaultStreamWithUsers(
address recipient,
address sender
)
internal
virtual
returns (uint256 streamId);
}
Loading
Loading