From b7b030e23bbada021a97528b6c5e63828b3458be Mon Sep 17 00:00:00 2001 From: Dristpunk Date: Tue, 19 Dec 2023 19:43:32 +0300 Subject: [PATCH] test: separate contracts and tests --- .../contracts/LockupDynamicStreamCreator.sol | 60 ++++++++++++++ .../MockLockupDynamicStreamCreator.sol | 48 +++++++++++ .../WonderLockupDynamicStreamCreator.sol | 80 +++++++++++++++++++ .../LockupDynamicStreamCreator.t.sol | 75 +++-------------- .../MockLockupDynamicStreamCreator.t.sol | 37 +++++++++ .../WonderLockupDynamicStreamCreator.t.sol | 40 ++++++++++ 6 files changed, 274 insertions(+), 66 deletions(-) create mode 100644 solidity/contracts/LockupDynamicStreamCreator.sol create mode 100644 solidity/contracts/MockLockupDynamicStreamCreator.sol create mode 100644 solidity/contracts/WonderLockupDynamicStreamCreator.sol create mode 100644 solidity/test/integration/MockLockupDynamicStreamCreator.t.sol create mode 100644 solidity/test/integration/WonderLockupDynamicStreamCreator.t.sol diff --git a/solidity/contracts/LockupDynamicStreamCreator.sol b/solidity/contracts/LockupDynamicStreamCreator.sol new file mode 100644 index 0000000..86f8344 --- /dev/null +++ b/solidity/contracts/LockupDynamicStreamCreator.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.19; + +import {ISablierV2LockupDynamic} from '@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol'; +import {Broker, LockupDynamic} from '@sablier/v2-core/src/types/DataTypes.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {ud2x18} from '@prb/math/src/UD2x18.sol'; +import {ud60x18} from '@prb/math/src/UD60x18.sol'; + +/// @notice Example of how to create a Lockup Dynamic stream. +/// @dev This code is referenced in the docs: https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-dynamic +contract LockupDynamicStreamCreator { + IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + ISablierV2LockupDynamic public immutable lockupDynamic; + + constructor(ISablierV2LockupDynamic lockupDynamic_) { + lockupDynamic = lockupDynamic_; + } + + function createLockupDynamicStream(uint256 amount0, uint256 amount1) public returns (uint256 streamId) { + // Sum the segment amounts + uint256 totalAmount = amount0 + amount1; + + // Transfer the provided amount of DAI tokens to this contract + DAI.transferFrom(msg.sender, address(this), totalAmount); + + // Approve the Sablier contract to spend DAI + DAI.approve(address(lockupDynamic), totalAmount); + + // Declare the params struct + LockupDynamic.CreateWithMilestones memory params; + + // Declare the function parameters + params.sender = msg.sender; // The sender will be able to cancel the stream + params.recipient = address(0xcafe); // The recipient of the streamed assets + params.totalAmount = uint128(totalAmount); // Total amount is the amount inclusive of all fees + params.asset = DAI; // The streaming asset + params.cancelable = true; // Whether the stream will be cancelable or not + params.startTime = uint40(block.timestamp + 100 seconds); + params.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined + + // Declare some dummy segments + params.segments = new LockupDynamic.Segment[](2); + params.segments[0] = LockupDynamic.Segment({ + amount: uint128(amount0), + exponent: ud2x18(1e18), + milestone: uint40(block.timestamp + 4 weeks) + }); + params.segments[1] = ( + LockupDynamic.Segment({ + amount: uint128(amount1), + exponent: ud2x18(3.14e18), + milestone: uint40(block.timestamp + 52 weeks) + }) + ); + + // Create the LockupDynamic stream + streamId = lockupDynamic.createWithMilestones(params); + } +} diff --git a/solidity/contracts/MockLockupDynamicStreamCreator.sol b/solidity/contracts/MockLockupDynamicStreamCreator.sol new file mode 100644 index 0000000..dc7ad60 --- /dev/null +++ b/solidity/contracts/MockLockupDynamicStreamCreator.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.19; + +import {ISablierV2LockupDynamic} from '@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol'; +import {Broker, LockupDynamic} from '@sablier/v2-core/src/types/DataTypes.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {ud2x18} from '@prb/math/src/UD2x18.sol'; +import {ud60x18} from '@prb/math/src/UD60x18.sol'; + +/// @notice Example of how to create a Lockup Dynamic stream. +/// @dev This code is referenced in the docs: https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-dynamic +contract MockLockupDynamicStreamCreator { + IERC20 public constant ASSET = IERC20(0x17422D756cE9024CC3fe7569f64941010eF277Db); + uint128 public TOTAL_AMOUNT = 2_000_000_000_000_000_000_000; + ISablierV2LockupDynamic public immutable lockupDynamic; + + constructor(ISablierV2LockupDynamic lockupDynamic_) { + lockupDynamic = lockupDynamic_; + } + + function createLockupDynamicStream() public returns (uint256 streamId) { + // Approve the Sablier contract to spend DAI + ASSET.approve(address(lockupDynamic), TOTAL_AMOUNT); + + // Declare the params struct + LockupDynamic.CreateWithMilestones memory params; + + // Declare the function parameters + params.sender = 0x54E0119cf6B50bD96a18c99f5227cce602097623; // The sender will be able to cancel the stream + params.recipient = 0x4dC83b54500236a1BDd5d3c503e753609d71e371; // The recipient of the streamed assets + params.totalAmount = TOTAL_AMOUNT; // Total amount is the amount inclusive of all fees + params.asset = ASSET; // The streaming asset + params.cancelable = true; // Whether the stream will be cancelable or not + params.startTime = 1_699_027_200; + params.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined + + // Declare some dummy segments + params.segments = new LockupDynamic.Segment[](1); + params.segments[0] = LockupDynamic.Segment({ + amount: TOTAL_AMOUNT, + exponent: ud2x18(3_000_000_000_000_000_000), + milestone: 1_702_483_200 + }); + + // Create the LockupDynamic stream + streamId = lockupDynamic.createWithMilestones(params); + } +} diff --git a/solidity/contracts/WonderLockupDynamicStreamCreator.sol b/solidity/contracts/WonderLockupDynamicStreamCreator.sol new file mode 100644 index 0000000..1874ff1 --- /dev/null +++ b/solidity/contracts/WonderLockupDynamicStreamCreator.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.19; + +import {ISablierV2LockupDynamic} from '@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol'; +import {Broker, LockupDynamic} from '@sablier/v2-core/src/types/DataTypes.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {ud2x18} from '@prb/math/src/UD2x18.sol'; +import {ud60x18} from '@prb/math/src/UD60x18.sol'; +import {Test} from 'forge-std/Test.sol'; +import {console} from 'forge-std/Console.sol'; + +/// @notice Example of how to create a Lockup Dynamic stream. +/// @dev This code is referenced in the docs: https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-dynamic +contract WonderLockupDynamicStreamCreator { + address public constant NEXT_MAINNET = 0xFE67A4450907459c3e1FFf623aA927dD4e28c67a; + + ISablierV2LockupDynamic internal lockupDynamic; + + constructor(ISablierV2LockupDynamic lockupDynamic_) { + lockupDynamic = lockupDynamic_; + } + + function createLockupDynamicStream( + uint256 amount0, + uint256 amount1, + uint256 amount2 + ) public returns (uint256 streamId) { + // Sum the segment amounts + uint256 totalAmount = amount0 + amount1 + amount2; + + // Transfer the provided amount of DAI tokens to this contract + IERC20(NEXT_MAINNET).transferFrom(msg.sender, address(this), totalAmount); + + // Approve the Sablier contract to spend DAI + IERC20(NEXT_MAINNET).approve(address(lockupDynamic), totalAmount); + + console.log('HERE'); + + // Declare the params struct + LockupDynamic.CreateWithMilestones memory params; + + // Declare the function parameters + params.sender = msg.sender; // The sender will be able to cancel the stream + params.recipient = address(0xcafe); // The recipient of the streamed assets + params.totalAmount = uint128(totalAmount); // Total amount is the amount inclusive of all fees + params.asset = IERC20(NEXT_MAINNET); // The streaming asset + params.cancelable = true; // Whether the stream will be cancelable or not + params.startTime = uint40(block.timestamp); + params.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined + + // Declare some dummy segments + params.segments = new LockupDynamic.Segment[](3); + params.segments[0] = LockupDynamic.Segment({ + amount: uint128(amount0), + exponent: ud2x18(0), // unlock at milestone + milestone: uint40(block.timestamp + 365 days) // 12 months + }); + params.segments[1] = ( + LockupDynamic.Segment({ + amount: uint128(amount1), + exponent: ud2x18(1e18), // linear unlock + milestone: uint40(block.timestamp + 365 days + 90 days) // ~15 months + }) + ); + params.segments[2] = ( + LockupDynamic.Segment({ + amount: uint128(amount2), + exponent: ud2x18(1e18), // linear unlock + milestone: uint40(block.timestamp + (365 days * 4)) // 48 months + }) + ); + + console.log('HERE'); + + // Create the LockupDynamic stream + streamId = lockupDynamic.createWithMilestones(params); + + console.log('HERE'); + } +} diff --git a/solidity/test/integration/LockupDynamicStreamCreator.t.sol b/solidity/test/integration/LockupDynamicStreamCreator.t.sol index d7e596b..0463956 100644 --- a/solidity/test/integration/LockupDynamicStreamCreator.t.sol +++ b/solidity/test/integration/LockupDynamicStreamCreator.t.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.19; import {ISablierV2LockupDynamic} from '@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol'; +import {LockupDynamicStreamCreator} from 'contracts/LockupDynamicStreamCreator.sol'; import {Broker, LockupDynamic} from '@sablier/v2-core/src/types/DataTypes.sol'; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {ud2x18} from '@prb/math/src/UD2x18.sol'; @@ -15,85 +16,27 @@ contract LockupDynamicStreamCreatorTest is Test { address internal _owner = makeAddr('owner'); address constant SABLIER_DYNAMIC_MAINNET = 0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44; - address constant NEXT_MAINNET = 0xFE67A4450907459c3e1FFf623aA927dD4e28c67a; - - address constant SABLIER_DYNAMIC_GOERLI = 0x4BE70EDe968e9dBA12DB42b9869Bec66bEDC17d7; - address constant NEXT_GOERLI = 0x7ea6eA49B0b0Ae9c5db7907d139D9Cd3439862a1; + address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; uint256 constant TOTAL_AMOUNT = 24_960_000 ether; ISablierV2LockupDynamic internal lockupDynamic; + LockupDynamicStreamCreator internal creator; function setUp() public { // mint some NEXT tokens - vm.createSelectFork(vm.rpcUrl('mainnet'), 18_820_753); - deal(NEXT_MAINNET, _owner, TOTAL_AMOUNT); + vm.createSelectFork(vm.rpcUrl('mainnet'), 18_820_679); + creator = new LockupDynamicStreamCreator(ISablierV2LockupDynamic(SABLIER_DYNAMIC_MAINNET)); + deal(DAI, _owner, TOTAL_AMOUNT); vm.prank(_owner); - IERC20(NEXT_MAINNET).approve(address(this), TOTAL_AMOUNT); + IERC20(DAI).approve(address(creator), TOTAL_AMOUNT); lockupDynamic = ISablierV2LockupDynamic(SABLIER_DYNAMIC_MAINNET); } function test_Creation() external { - uint256 streamId = createLockupDynamicStream(1_920_000, 5_917_440, 17_122_560); + vm.prank(_owner); + uint256 streamId = creator.createLockupDynamicStream(1000, 2000); // 1_920_000, 5_917_440, 17_122_560 console.log('streamId: %s', streamId); } - - function createLockupDynamicStream( - uint256 amount0, - uint256 amount1, - uint256 amount2 - ) public returns (uint256 streamId) { - // Sum the segment amounts - uint256 totalAmount = amount0 + amount1 + amount2; - - // Transfer the provided amount of DAI tokens to this contract - IERC20(NEXT_MAINNET).transferFrom(_owner, address(this), totalAmount); - - // Approve the Sablier contract to spend DAI - IERC20(NEXT_MAINNET).approve(address(lockupDynamic), totalAmount); - - console.log('HERE'); - - // Declare the params struct - LockupDynamic.CreateWithMilestones memory params; - - // Declare the function parameters - params.sender = _owner; // The sender will be able to cancel the stream - params.recipient = address(0xcafe); // The recipient of the streamed assets - params.totalAmount = uint128(totalAmount); // Total amount is the amount inclusive of all fees - params.asset = IERC20(NEXT_MAINNET); // The streaming asset - params.cancelable = true; // Whether the stream will be cancelable or not - params.startTime = uint40(block.timestamp); - params.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined - - // Declare some dummy segments - params.segments = new LockupDynamic.Segment[](3); - params.segments[0] = LockupDynamic.Segment({ - amount: uint128(amount0), - exponent: ud2x18(0), // unlock at milestone - milestone: uint40(block.timestamp + 365 days) // 12 months - }); - params.segments[1] = ( - LockupDynamic.Segment({ - amount: uint128(amount1), - exponent: ud2x18(1e18), // linear unlock - milestone: uint40(block.timestamp + 365 days + 90 days) // ~15 months - }) - ); - params.segments[2] = ( - LockupDynamic.Segment({ - amount: uint128(amount2), - exponent: ud2x18(1e18), // linear unlock - milestone: uint40(block.timestamp + (365 days * 4)) // 48 months - }) - ); - - console.log('HERE'); - - // Create the LockupDynamic stream - streamId = lockupDynamic.createWithMilestones(params); - - console.log('HERE'); - } } diff --git a/solidity/test/integration/MockLockupDynamicStreamCreator.t.sol b/solidity/test/integration/MockLockupDynamicStreamCreator.t.sol new file mode 100644 index 0000000..3bf2d16 --- /dev/null +++ b/solidity/test/integration/MockLockupDynamicStreamCreator.t.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.19; + +import {ISablierV2LockupDynamic} from '@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol'; +import {MockLockupDynamicStreamCreator} from 'contracts/MockLockupDynamicStreamCreator.sol'; +import {Broker, LockupDynamic} from '@sablier/v2-core/src/types/DataTypes.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {ud2x18} from '@prb/math/src/UD2x18.sol'; +import {ud60x18} from '@prb/math/src/UD60x18.sol'; +import {Test} from 'forge-std/Test.sol'; +import {console} from 'forge-std/Console.sol'; + +/// @notice Example of how to create a Lockup Dynamic stream. +/// @dev This code is referenced in the docs: https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-dynamic +contract MockLockupDynamicStreamCreatorTest is Test { + address internal _owner = makeAddr('owner'); + + address constant SABLIER_DYNAMIC_MAINNET = 0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44; + + ISablierV2LockupDynamic internal lockupDynamic; + MockLockupDynamicStreamCreator internal creator; + + function setUp() public { + // mint some NEXT tokens + vm.createSelectFork(vm.rpcUrl('mainnet'), 18_820_679); + creator = new MockLockupDynamicStreamCreator(ISablierV2LockupDynamic(SABLIER_DYNAMIC_MAINNET)); + deal(address(creator.ASSET()), address(creator), creator.TOTAL_AMOUNT()); + + lockupDynamic = ISablierV2LockupDynamic(SABLIER_DYNAMIC_MAINNET); + } + + function test_Creation() external { + vm.prank(_owner); + uint256 streamId = creator.createLockupDynamicStream(); + console.log('streamId: %s', streamId); + } +} diff --git a/solidity/test/integration/WonderLockupDynamicStreamCreator.t.sol b/solidity/test/integration/WonderLockupDynamicStreamCreator.t.sol new file mode 100644 index 0000000..cdf4f99 --- /dev/null +++ b/solidity/test/integration/WonderLockupDynamicStreamCreator.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.19; + +import {ISablierV2LockupDynamic} from '@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol'; +import {Broker, LockupDynamic} from '@sablier/v2-core/src/types/DataTypes.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {ud2x18} from '@prb/math/src/UD2x18.sol'; +import {ud60x18} from '@prb/math/src/UD60x18.sol'; +import {Test} from 'forge-std/Test.sol'; +import {console} from 'forge-std/Console.sol'; +import {WonderLockupDynamicStreamCreator} from 'contracts/WonderLockupDynamicStreamCreator.sol'; + +/// @notice Example of how to create a Lockup Dynamic stream. +/// @dev This code is referenced in the docs: https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-dynamic +contract WonderLockupDynamicStreamCreatorTest is Test { + address internal _owner = makeAddr('owner'); + WonderLockupDynamicStreamCreator internal creator; + + address public constant SABLIER_DYNAMIC_MAINNET = 0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44; + uint256 public constant TOTAL_AMOUNT = 24_960_000 ether; + + function setUp() public { + // mint some NEXT tokens + vm.createSelectFork(vm.rpcUrl('mainnet'), 18_820_679); + creator = new WonderLockupDynamicStreamCreator(ISablierV2LockupDynamic(SABLIER_DYNAMIC_MAINNET)); + deal(creator.NEXT_MAINNET(), _owner, TOTAL_AMOUNT); + vm.prank(_owner); + IERC20(creator.NEXT_MAINNET()).approve(address(creator), TOTAL_AMOUNT); + } + + function test_Creation() external { + assertEq(TOTAL_AMOUNT, 1_920_000 ether + 5_917_440 ether + 17_122_560 ether); + assertEq(IERC20(creator.NEXT_MAINNET()).balanceOf(_owner), TOTAL_AMOUNT); + assertEq(IERC20(creator.NEXT_MAINNET()).allowance(_owner, address(creator)), TOTAL_AMOUNT); + console.log('here'); + vm.prank(_owner); + uint256 streamId = creator.createLockupDynamicStream(1_920_000 ether, 5_917_440 ether, 17_122_560 ether); + console.log('streamId: %s', streamId); + } +}