Skip to content

Commit

Permalink
feat(invoice-module): add Sablier V2 Lockup Linear stream management
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielstoica committed Jul 9, 2024
1 parent 5c422a1 commit 95c6f55
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
109 changes: 109 additions & 0 deletions src/modules/invoice-module/LockupStreamCreator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ud60x18, UD60x18 } from "@prb/math/src/UD60x18.sol";
import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol";
import { ILockupStreamCreator } from "./interfaces/ILockupStreamCreator.sol";
import { Broker, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol";
import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol";
import { Errors } from "./libraries/Errors.sol";

/// @title LockupStreamCreator
/// @dev See the documentation in {ILockupStreamCreator}
contract LockupStreamCreator is ILockupStreamCreator {
/*//////////////////////////////////////////////////////////////////////////
PUBLIC STORAGE
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc ILockupStreamCreator
ISablierV2LockupLinear public immutable override LOCKUP_LINEAR;

/// @inheritdoc ILockupStreamCreator
address public override brokerAdmin;

/// @inheritdoc ILockupStreamCreator
UD60x18 public brokerFee;

/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/

/// @dev Initializes the address of the {SablierV2LockupLinear} contract and the address of the broker admin account or contract
constructor(address _sablierLockupDeployment, address _brokerAdmin) {
LOCKUP_LINEAR = ISablierV2LockupLinear(_sablierLockupDeployment);
brokerAdmin = _brokerAdmin;
}

/*//////////////////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Reverts if the `msg.sender` is not the broker admin account or contract
modifier onlyBrokerAdmin() {
if (msg.sender != brokerAdmin) revert Errors.OnlyBrokerAdmin();
_;
}

/*//////////////////////////////////////////////////////////////////////////
NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @dev Creates either a Lockup Linear or Dynamic stream
function createStream(
IERC20 asset,
uint128 totalAmount,
LockupLinear.Durations memory durations,
address recipient
) public returns (uint256 streamId) {
// Transfer the provided amount of ERC-20 tokens to this contract
asset.transferFrom(msg.sender, address(this), totalAmount);

// Approve the Sablier contract to spend the ERC-20 tokens
asset.approve(address(LOCKUP_LINEAR), totalAmount);

// Create the Lockup Linear stream
streamId = _createLinearStream(asset, totalAmount, durations, recipient);
}

/// @dev Updates the fee charged by the broker
function updateBrokerFee(UD60x18 newBrokerFee) public onlyBrokerAdmin {
// Log the broker fee update
emit BrokerFeeUpdated({ oldFee: brokerFee, newFee: newBrokerFee });

// Update the fee charged by the broker
brokerFee = newBrokerFee;
}

/*//////////////////////////////////////////////////////////////////////////
INTERNAL-METHODS
//////////////////////////////////////////////////////////////////////////*/

/// @dev Creates a Lockup Linear stream
/// See https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-linear
function _createLinearStream(
IERC20 asset,
uint128 totalAmount,
LockupLinear.Durations memory durations,
address recipient
) internal returns (uint256 streamId) {
// Declare the params struct
LockupLinear.CreateWithDurations memory params;

// Declare the function parameters
params.sender = msg.sender; // The sender will be able to cancel the stream
params.recipient = recipient; // The recipient of the streamed assets
params.totalAmount = totalAmount; // 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.transferable = true; // Whether the stream will be transferable or not
params.durations = LockupLinear.Durations({
cliff: durations.cliff, // Assets will be unlocked only after x period of time
total: durations.total // Setting a total duration of x period of time
});
params.broker = Broker({ account: brokerAdmin, fee: brokerFee }); // Optional parameter for charging a fee

// Create the LockupLinear stream using a function that sets the start time to `block.timestamp`
streamId = LOCKUP_LINEAR.createWithDurations(params);
}
}
35 changes: 35 additions & 0 deletions src/modules/invoice-module/interfaces/ILockupStreamCreator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol";
import { UD60x18 } from "@prb/math/src/UD60x18.sol";

/// @title ILockupStreamCreator
/// @notice Contract used to create Sablier V2 compatible streams
/// @dev This code is referenced in the docs: https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-linear
interface ILockupStreamCreator {
/*//////////////////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Emitted when the broker fee is updated
/// @param oldFee The old broker fee
/// @param newFee The new broker fee
event BrokerFeeUpdated(UD60x18 oldFee, UD60x18 newFee);

/*//////////////////////////////////////////////////////////////////////////
CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice The address of the {SablierV2LockupLinear} contract used to create linear streams
/// @dev This is initialized at construction time and it might be different depending on the deployment chain
/// See https://docs.sablier.com/contracts/v2/deployments
function LOCKUP_LINEAR() external view returns (ISablierV2LockupLinear);

/// @notice The address of the broker admin account or contract managing the broker fee
function brokerAdmin() external view returns (address);

/// @notice The broker fee charged to create Sablier V2 stream
/// @dev See the `UD60x18` type definition in the `@prb/math/src/ud60x18/ValueType.sol file`
function brokerFee() external view returns (UD60x18);
}

0 comments on commit 95c6f55

Please sign in to comment.