Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/GYFIStreamMainWaveTwo.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ud60x18} from "@prb/math/src/UD60x18.sol";
import {ISablierV2LockupTranched} from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol";
import {Broker, LockupTranched} from "@sablier/v2-core/src/types/DataTypes.sol";
/// @dev Similar code is referenced in the docs: https://docs.sablier.com/contracts/v2/guides/create-stream/lockup-tranched
contract GYFIStreamMainWaveTwo {
IERC20 public immutable gyfi;
ISablierV2LockupTranched public immutable lockupTranched;
address public immutable owner;
uint256 internal constant ONE = 1e18; // 18 decimal places
constructor(address owner_, IERC20 gyfi_, ISablierV2LockupTranched lockupTranched_) {
owner = owner_;
gyfi = gyfi_;
lockupTranched = lockupTranched_;
}
function mulDown(uint256 a, uint256 b) internal pure returns (uint128) {
return uint128((a * b) / ONE);
}
/// @dev Note: sender must have first approved this contract to spend GYFI
function createStream(uint128 totalGYFI, address beneficiary)
public
returns (uint256 streamId, LockupTranched.CreateWithTimestamps memory params)
{
// Transfer the provided amount of GYFI tokens to this contract
gyfi.transferFrom(msg.sender, address(this), totalGYFI);
// Approve the Sablier contract to spend GYFI
gyfi.approve(address(lockupTranched), totalGYFI);
// Declare the function parameters
params.sender = owner; // sender can cancel the stream
params.recipient = beneficiary; // The recipient of the streamed assets
params.totalAmount = uint128(totalGYFI); // Total amount is the amount inclusive of all fees
params.asset = gyfi; // 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.startTime = uint40(1745989200); // Wed Apr 30 2025 00:00:00 GMT-0500 (Eastern Standard Time)
params.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined
// Create tranches
params.tranches = new LockupTranched.Tranche[](21);
params.tranches[0] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 2857 * 10 ** 14), timestamp: 1761886800}); // Fri Oct 31 2025 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[1] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1764478800}); // Sun Nov 30 2025 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[2] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1767157200}); // Wed Dec 31 2025 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[3] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 358 * 10 ** 14), timestamp: 1769835600}); // Sat Jan 31 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[4] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1772254800}); // Sat Feb 28 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[5] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1774933200}); // Tue Mar 31 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[6] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1777525200}); // Thu Apr 30 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[7] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1780203600}); // Sun May 31 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[8] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1782795600}); // Tue Jun 30 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[9] = LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1785474000}); // Fri Jul 31 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[10] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 358 * 10 ** 14), timestamp: 1788152400}); // Mon Aug 31 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[11] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1790744400}); // Wed Sep 30 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[12] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1793422800}); // Sat Oct 31 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[13] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1796014800}); // Mon Nov 30 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[14] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1798693200}); // Thu Dec 31 2026 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[15] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1801371600}); // Sun Jan 31 2027 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[16] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1803790800}); // Sun Feb 28 2027 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[17] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 358 * 10 ** 14), timestamp: 1806469200}); // Wed Mar 31 2027 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[18] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1809061200}); // Fri Apr 30 2027 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[19] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1811739600}); // Mon May 31 2027 00:00:00 GMT-0500 (Eastern Standard Time)
params.tranches[20] =
LockupTranched.Tranche({amount: mulDown(totalGYFI, 357 * 10 ** 14), timestamp: 1814331600}); // Wed Jun 30 2027 00:00:00 GMT-0500 (Eastern Standard Time)
uint256 totalDistributed;
for (uint256 i = 0; i < params.tranches.length; i++) {
totalDistributed += params.tranches[i].amount;
}
require(totalDistributed == totalGYFI, "total distributed amount does not match totalGYFI");
// Create the LockupTranched stream
streamId = lockupTranched.createWithTimestamps(params);
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"lib/prb-math/src/UD60x18.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
/*
██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗ █████╗
██║ ██║██╔══██╗██╔════╝ ██╔═████╗╚██╗██╔╝███║██╔══██╗
██║ ██║██║ ██║███████╗ ██║██╔██║ ╚███╔╝ ╚██║╚█████╔╝
██║ ██║██║ ██║██╔═══██╗████╔╝██║ ██╔██╗ ██║██╔══██╗
╚██████╔╝██████╔╝╚██████╔╝╚██████╔╝██╔╝ ██╗ ██║╚█████╔╝
╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚════╝
*/
import "./ud60x18/Casting.sol";
import "./ud60x18/Constants.sol";
import "./ud60x18/Conversions.sol";
import "./ud60x18/Errors.sol";
import "./ud60x18/Helpers.sol";
import "./ud60x18/Math.sol";
import "./ud60x18/ValueType.sol";
"
},
"lib/v2-core/src/interfaces/ISablierV2LockupTranched.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Lockup, LockupTranched } from "../types/DataTypes.sol";
import { ISablierV2Lockup } from "./ISablierV2Lockup.sol";
/// @title ISablierV2LockupTranched
/// @notice Creates and manages Lockup streams with a tranched distribution function.
interface ISablierV2LockupTranched is ISablierV2Lockup {
/*//////////////////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Emitted when a stream is created.
/// @param streamId The ID of the newly created stream.
/// @param funder The address which has funded the stream.
/// @param sender The address distributing the assets, which will have the ability to cancel the stream.
/// @param recipient The address toward which to stream the assets.
/// @param amounts Struct containing (i) the deposit amount, and (ii) the broker fee amount, both denoted
/// in units of the asset's decimals.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param cancelable Boolean indicating whether the stream will be cancelable or not.
/// @param transferable Boolean indicating whether the stream NFT is transferable or not.
/// @param tranches The tranches the protocol uses to compose the tranched distribution function.
/// @param timestamps Struct containing (i) the stream's start time and (ii) end time, both as Unix timestamps.
/// @param broker The address of the broker who has helped create the stream, e.g. a front-end website.
event CreateLockupTranchedStream(
uint256 streamId,
address funder,
address indexed sender,
address indexed recipient,
Lockup.CreateAmounts amounts,
IERC20 indexed asset,
bool cancelable,
bool transferable,
LockupTranched.Tranche[] tranches,
LockupTranched.Timestamps timestamps,
address broker
);
/*//////////////////////////////////////////////////////////////////////////
CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Retrieves the full stream details.
/// @dev Reverts if `streamId` references a null stream.
/// @param streamId The stream ID for the query.
/// @return stream See the documentation in {DataTypes}.
function getStream(uint256 streamId) external view returns (LockupTranched.StreamLT memory stream);
/// @notice Retrieves the stream's start and end timestamps.
/// @dev Reverts if `streamId` references a null stream.
/// @param streamId The stream ID for the query.
/// @return timestamps See the documentation in {DataTypes}.
function getTimestamps(uint256 streamId) external view returns (LockupTranched.Timestamps memory timestamps);
/// @notice Retrieves the tranches used to compose the tranched distribution function.
/// @dev Reverts if `streamId` references a null stream.
/// @param streamId The stream ID for the query.
function getTranches(uint256 streamId) external view returns (LockupTranched.Tranche[] memory tranches);
/// @notice The maximum number of tranches allowed in a stream.
/// @dev This is initialized at construction time and cannot be changed later.
function MAX_TRANCHE_COUNT() external view returns (uint256);
/*//////////////////////////////////////////////////////////////////////////
NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of
/// `block.timestamp` and all specified time durations. The tranche timestamps are derived from these
/// durations. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT.
///
/// @dev Emits a {Transfer} and {CreateLockupTrancheStream} event.
///
/// Requirements:
/// - All requirements in {createWithTimestamps} must be met for the calculated parameters.
///
/// @param params Struct encapsulating the function parameters, which are documented in {DataTypes}.
/// @return streamId The ID of the newly created stream.
function createWithDurations(LockupTranched.CreateWithDurations calldata params)
external
returns (uint256 streamId);
/// @notice Creates a stream with the provided tranche timestamps, implying the end time from the last timestamp.
/// The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT.
///
/// @dev Emits a {Transfer} and {CreateLockupTrancheStream} event.
///
/// Notes:
/// - As long as the tranche timestamps are arranged in ascending order, it is not an error for some
/// of them to be in the past.
///
/// Requirements:
/// - Must not be delegate called.
/// - `params.totalAmount` must be greater than zero.
/// - If set, `params.broker.fee` must not be greater than `MAX_BROKER_FEE`.
/// - `params.startTime` must be greater than zero and less than the first tranche's timestamp.
/// - `params.tranches` must have at least one tranche, but not more than `MAX_TRANCHE_COUNT`.
/// - The tranche timestamps must be arranged in ascending order.
/// - The last tranche timestamp (i.e. the stream's end time) must be in the future.
/// - The sum of the tranche amounts must equal the deposit amount.
/// - `params.recipient` must not be the zero address.
/// - `msg.sender` must have allowed this contract to spend at least `params.totalAmount` assets.
///
/// @param params Struct encapsulating the function parameters, which are documented in {DataTypes}.
/// @return streamId The ID of the newly created stream.
function createWithTimestamps(LockupTranched.CreateWithTimestamps calldata params)
external
returns (uint256 streamId);
}
"
},
"lib/v2-core/src/types/DataTypes.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;
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";
// DataTypes.sol
//
// This file defines all structs used in V2 Core, most of which are organized under three namespaces:
//
// - Lockup
// - LockupDynamic
// - LockupLinear
// - LockupTranched
//
// You will notice that some structs contain "slot" annotations - they are used to indicate the
// storage layout of the struct. It is more gas efficient to group small data types together so
// that they fit in a single 32-byte slot.
/// @notice Struct encapsulating the broker parameters passed to the create functions. Both can be set to zero.
/// @param account The address receiving the broker's fee.
/// @param fee The broker's percentage fee from the total amount, denoted as a fixed-point number where 1e18 is 100%.
struct Broker {
address account;
UD60x18 fee;
}
/// @notice Namespace for the structs used in both {SablierV2LockupLinear} and {SablierV2LockupDynamic}.
library Lockup {
/// @notice Struct encapsulating the deposit, withdrawn, and refunded amounts, both denoted in units of the asset's
/// decimals.
/// @dev Because the deposited and the withdrawn amount are often read together, declaring them in the same slot
/// saves gas.
/// @param deposited The initial amount deposited in the stream, net of broker fee.
/// @param withdrawn The cumulative amount withdrawn from the stream.
/// @param refunded The amount refunded to the sender. Unless the stream was canceled, this is always zero.
struct Amounts {
// slot 0
uint128 deposited;
uint128 withdrawn;
// slot 1
uint128 refunded;
}
/// @notice Struct encapsulating the deposit amount and the broker fee amount, both denoted in units of the asset's
/// decimals.
/// @param deposit The amount to deposit in the stream.
/// @param brokerFee The broker fee amount.
struct CreateAmounts {
uint128 deposit;
uint128 brokerFee;
}
/// @notice Enum representing the different statuses of a stream.
/// @custom:value0 PENDING Stream created but not started; assets are in a pending state.
/// @custom:value1 STREAMING Active stream where assets are currently being streamed.
/// @custom:value2 SETTLED All assets have been streamed; recipient is due to withdraw them.
/// @custom:value3 CANCELED Canceled stream; remaining assets await recipient's withdrawal.
/// @custom:value4 DEPLETED Depleted stream; all assets have been withdrawn and/or refunded.
enum Status {
PENDING,
STREAMING,
SETTLED,
CANCELED,
DEPLETED
}
/// @notice A common data structure to be stored in all {SablierV2Lockup} models.
/// @dev The fields are arranged like this to save gas via tight variable packing.
/// @param sender The address distributing the assets, with the ability to cancel the stream.
/// @param startTime The Unix timestamp indicating the stream's start.
/// @param endTime The Unix timestamp indicating the stream's end.
/// @param isCancelable Boolean indicating if the stream is cancelable.
/// @param wasCanceled Boolean indicating if the stream was canceled.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param isDepleted Boolean indicating if the stream is depleted.
/// @param isStream Boolean indicating if the struct entity exists.
/// @param isTransferable Boolean indicating if the stream NFT is transferable.
/// @param amounts Struct containing the deposit, withdrawn, and refunded amounts, both denoted in units of the
/// asset's decimals.
struct Stream {
// slot 0
address sender;
uint40 startTime;
uint40 endTime;
bool isCancelable;
bool wasCanceled;
// slot 1
IERC20 asset;
bool isDepleted;
bool isStream;
bool isTransferable;
// slot 2 and 3
Lockup.Amounts amounts;
}
}
/// @notice Namespace for the structs used in {SablierV2LockupDynamic}.
library LockupDynamic {
/// @notice Struct encapsulating the parameters of the {SablierV2LockupDynamic.createWithDurations} function.
/// @param sender The address distributing the assets, with the ability to cancel the stream. It doesn't have to be
/// the same as `msg.sender`.
/// @param recipient The address receiving the assets.
/// @param totalAmount The total amount of ERC-20 assets to be distributed, including the stream deposit and any
/// broker fee, both denoted in units of the asset's decimals.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param cancelable Indicates if the stream is cancelable.
/// @param transferable Indicates if the stream NFT is transferable.
/// @param segments Segments with durations used to compose the dynamic distribution function. Timestamps are
/// calculated by starting from `block.timestamp` and adding each duration to the previous timestamp.
/// @param broker Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the
/// percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero.
struct CreateWithDurations {
address sender;
address recipient;
uint128 totalAmount;
IERC20 asset;
bool cancelable;
bool transferable;
SegmentWithDuration[] segments;
Broker broker;
}
/// @notice Struct encapsulating the parameters of the {SablierV2LockupDynamic.createWithTimestamps} function.
/// @param sender The address distributing the assets, with the ability to cancel the stream. It doesn't have to be
/// the same as `msg.sender`.
/// @param recipient The address receiving the assets.
/// @param totalAmount The total amount of ERC-20 assets to be distributed, including the stream deposit and any
/// broker fee, both denoted in units of the asset's decimals.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param cancelable Indicates if the stream is cancelable.
/// @param transferable Indicates if the stream NFT is transferable.
/// @param startTime The Unix timestamp indicating the stream's start.
/// @param segments Segments used to compose the dynamic distribution function.
/// @param broker Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the
/// percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero.
struct CreateWithTimestamps {
address sender;
address recipient;
uint128 totalAmount;
IERC20 asset;
bool cancelable;
bool transferable;
uint40 startTime;
Segment[] segments;
Broker broker;
}
/// @notice Segment struct used in the Lockup Dynamic stream.
/// @param amount The amount of assets to be streamed in the segment, denoted in units of the asset's decimals.
/// @param exponent The exponent of the segment, denoted as a fixed-point number.
/// @param timestamp The Unix timestamp indicating the segment's end.
struct Segment {
// slot 0
uint128 amount;
UD2x18 exponent;
uint40 timestamp;
}
/// @notice Segment struct used at runtime in {SablierV2LockupDynamic.createWithDurations}.
/// @param amount The amount of assets to be streamed in the segment, denoted in units of the asset's decimals.
/// @param exponent The exponent of the segment, denoted as a fixed-point number.
/// @param duration The time difference in seconds between the segment and the previous one.
struct SegmentWithDuration {
uint128 amount;
UD2x18 exponent;
uint40 duration;
}
/// @notice Struct encapsulating the full details of a stream.
/// @dev Extends `Lockup.Stream` by including the recipient and the segments.
struct StreamLD {
address sender;
address recipient;
uint40 startTime;
uint40 endTime;
bool isCancelable;
bool wasCanceled;
IERC20 asset;
bool isDepleted;
bool isStream;
bool isTransferable;
Lockup.Amounts amounts;
Segment[] segments;
}
/// @notice Struct encapsulating the LockupDynamic timestamps.
/// @param start The Unix timestamp indicating the stream's start.
/// @param end The Unix timestamp indicating the stream's end.
struct Timestamps {
uint40 start;
uint40 end;
}
}
/// @notice Namespace for the structs used in {SablierV2LockupLinear}.
library LockupLinear {
/// @notice Struct encapsulating the parameters of the {SablierV2LockupLinear.createWithDurations} function.
/// @param sender The address distributing the assets, with the ability to cancel the stream. It doesn't have to be
/// the same as `msg.sender`.
/// @param recipient The address receiving the assets.
/// @param totalAmount The total amount of ERC-20 assets to be distributed, including the stream deposit and any
/// broker fee, both denoted in units of the asset's decimals.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param cancelable Indicates if the stream is cancelable.
/// @param transferable Indicates if the stream NFT is transferable.
/// @param durations Struct containing (i) cliff period duration and (ii) total stream duration, both in seconds.
/// @param broker Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the
/// percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero.
struct CreateWithDurations {
address sender;
address recipient;
uint128 totalAmount;
IERC20 asset;
bool cancelable;
bool transferable;
Durations durations;
Broker broker;
}
/// @notice Struct encapsulating the parameters of the {SablierV2LockupLinear.createWithTimestamps} function.
/// @param sender The address distributing the assets, with the ability to cancel the stream. It doesn't have to be
/// the same as `msg.sender`.
/// @param recipient The address receiving the assets.
/// @param totalAmount The total amount of ERC-20 assets to be distributed, including the stream deposit and any
/// broker fee, both denoted in units of the asset's decimals.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param cancelable Indicates if the stream is cancelable.
/// @param transferable Indicates if the stream NFT is transferable.
/// @param timestamps Struct containing (i) the stream's start time, (ii) cliff time, and (iii) end time, all as
/// Unix timestamps.
/// @param broker Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the
/// percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero.
struct CreateWithTimestamps {
address sender;
address recipient;
uint128 totalAmount;
IERC20 asset;
bool cancelable;
bool transferable;
Timestamps timestamps;
Broker broker;
}
/// @notice Struct encapsulating the cliff duration and the total duration.
/// @param cliff The cliff duration in seconds.
/// @param total The total duration in seconds.
struct Durations {
uint40 cliff;
uint40 total;
}
/// @notice Struct encapsulating the full details of a stream.
/// @dev Extends `Lockup.Stream` by including the recipient and the cliff time.
struct StreamLL {
address sender;
address recipient;
uint40 startTime;
bool isCancelable;
bool wasCanceled;
IERC20 asset;
uint40 endTime;
bool isDepleted;
bool isStream;
bool isTransferable;
Lockup.Amounts amounts;
uint40 cliffTime;
}
/// @notice Struct encapsulating the LockupLinear timestamps.
/// @param start The Unix timestamp for the stream's start.
/// @param cliff The Unix timestamp for the cliff period's end. A value of zero means there is no cliff.
/// @param end The Unix timestamp for the stream's end.
struct Timestamps {
uint40 start;
uint40 cliff;
uint40 end;
}
}
/// @notice Namespace for the structs used in {SablierV2LockupTranched}.
library LockupTranched {
/// @notice Struct encapsulating the parameters of the {SablierV2LockupTranched.createWithDurations} function.
/// @param sender The address distributing the assets, with the ability to cancel the stream. It doesn't have to be
/// the same as `msg.sender`.
/// @param recipient The address receiving the assets.
/// @param totalAmount The total amount of ERC-20 assets to be distributed, including the stream deposit and any
/// broker fee, both denoted in units of the asset's decimals.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param cancelable Indicates if the stream is cancelable.
/// @param transferable Indicates if the stream NFT is transferable.
/// @param tranches Tranches with durations used to compose the tranched distribution function. Timestamps are
/// calculated by starting from `block.timestamp` and adding each duration to the previous timestamp.
/// @param broker Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the
/// percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero.
struct CreateWithDurations {
address sender;
address recipient;
uint128 totalAmount;
IERC20 asset;
bool cancelable;
bool transferable;
TrancheWithDuration[] tranches;
Broker broker;
}
/// @notice Struct encapsulating the parameters of the {SablierV2LockupTranched.createWithTimestamps} function.
/// @param sender The address distributing the assets, with the ability to cancel the stream. It doesn't have to be
/// the same as `msg.sender`.
/// @param recipient The address receiving the assets.
/// @param totalAmount The total amount of ERC-20 assets to be distributed, including the stream deposit and any
/// broker fee, both denoted in units of the asset's decimals.
/// @param asset The contract address of the ERC-20 asset to be distributed.
/// @param cancelable Indicates if the stream is cancelable.
/// @param transferable Indicates if the stream NFT is transferable.
/// @param startTime The Unix timestamp indicating the stream's start.
/// @param tranches Tranches used to compose the tranched distribution function.
/// @param broker Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the
/// percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero.
struct CreateWithTimestamps {
address sender;
address recipient;
uint128 totalAmount;
IERC20 asset;
bool cancelable;
bool transferable;
uint40 startTime;
Tranche[] tranches;
Broker broker;
}
/// @notice Struct encapsulating the full details of a stream.
/// @dev Extends `Lockup.Stream` by including the recipient and the tranches.
struct StreamLT {
address sender;
address recipient;
uint40 startTime;
uint40 endTime;
bool isCancelable;
bool wasCanceled;
IERC20 asset;
bool isDepleted;
bool isStream;
bool isTransferable;
Lockup.Amounts amounts;
Tranche[] tranches;
}
/// @notice Struct encapsulating the LockupTranched timestamps.
/// @param start The Unix timestamp indicating the stream's start.
/// @param end The Unix timestamp indicating the stream's end.
struct Timestamps {
uint40 start;
uint40 end;
}
/// @notice Tranche struct used in the Lockup Tranched stream.
/// @param amount The amount of assets to be unlocked in the tranche, denoted in units of the asset's decimals.
/// @param timestamp The Unix timestamp indicating the tranche's end.
struct Tranche {
// slot 0
uint128 amount;
uint40 timestamp;
}
/// @notice Tranche struct used at runtime in {SablierV2LockupTranched.createWithDurations}.
/// @param amount The amount of assets to be unlocked in the tranche, denoted in units of the asset's decimals.
/// @param duration The time difference in seconds between the tranche and the previous one.
struct TrancheWithDuration {
uint128 amount;
uint40 duration;
}
}
"
},
"lib/prb-math/src/ud60x18/Casting.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD59x18 } from "../sd59x18/Constants.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Casts a UD60x18 number into SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD60x18 x) pure returns (SD1x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uint256(int256(uMAX_SD1x18))) {
revert CastingErrors.PRBMath_UD60x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(uint64(xUint)));
}
/// @notice Casts a UD60x18 number into UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(UD60x18 x) pure returns (UD2x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uMAX_UD2x18) {
revert CastingErrors.PRBMath_UD60x18_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(xUint));
}
/// @notice Casts a UD60x18 number into SD59x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD59x18`.
function intoSD59x18(UD60x18 x) pure returns (SD59x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uint256(uMAX_SD59x18)) {
revert CastingErrors.PRBMath_UD60x18_IntoSD59x18_Overflow(x);
}
result = SD59x18.wrap(int256(xUint));
}
/// @notice Casts a UD60x18 number into uint128.
/// @dev This is basically an alias for {unwrap}.
function intoUint256(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x);
}
/// @notice Casts a UD60x18 number into uint128.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT128`.
function intoUint128(UD60x18 x) pure returns (uint128 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > MAX_UINT128) {
revert CastingErrors.PRBMath_UD60x18_IntoUint128_Overflow(x);
}
result = uint128(xUint);
}
/// @notice Casts a UD60x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD60x18 x) pure returns (uint40 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > MAX_UINT40) {
revert CastingErrors.PRBMath_UD60x18_IntoUint40_Overflow(x);
}
result = uint40(xUint);
}
/// @notice Alias for {wrap}.
function ud(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}
/// @notice Alias for {wrap}.
function ud60x18(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}
/// @notice Unwraps a UD60x18 number into uint256.
function unwrap(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x);
}
/// @notice Wraps a uint256 number into the UD60x18 value type.
function wrap(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}
"
},
"lib/prb-math/src/ud60x18/Constants.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD60x18 } from "./ValueType.sol";
// NOTICE: the "u" prefix stands for "unwrapped".
/// @dev Euler's number as a UD60x18 number.
UD60x18 constant E = UD60x18.wrap(2_718281828459045235);
/// @dev The maximum input permitted in {exp}.
uint256 constant uEXP_MAX_INPUT = 133_084258667509499440;
UD60x18 constant EXP_MAX_INPUT = UD60x18.wrap(uEXP_MAX_INPUT);
/// @dev The maximum input permitted in {exp2}.
uint256 constant uEXP2_MAX_INPUT = 192e18 - 1;
UD60x18 constant EXP2_MAX_INPUT = UD60x18.wrap(uEXP2_MAX_INPUT);
/// @dev Half the UNIT number.
uint256 constant uHALF_UNIT = 0.5e18;
UD60x18 constant HALF_UNIT = UD60x18.wrap(uHALF_UNIT);
/// @dev $log_2(10)$ as a UD60x18 number.
uint256 constant uLOG2_10 = 3_321928094887362347;
UD60x18 constant LOG2_10 = UD60x18.wrap(uLOG2_10);
/// @dev $log_2(e)$ as a UD60x18 number.
uint256 constant uLOG2_E = 1_442695040888963407;
UD60x18 constant LOG2_E = UD60x18.wrap(uLOG2_E);
/// @dev The maximum value a UD60x18 number can have.
uint256 constant uMAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935;
UD60x18 constant MAX_UD60x18 = UD60x18.wrap(uMAX_UD60x18);
/// @dev The maximum whole value a UD60x18 number can have.
uint256 constant uMAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000;
UD60x18 constant MAX_WHOLE_UD60x18 = UD60x18.wrap(uMAX_WHOLE_UD60x18);
/// @dev PI as a UD60x18 number.
UD60x18 constant PI = UD60x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of UD60x18.
uint256 constant uUNIT = 1e18;
UD60x18 constant UNIT = UD60x18.wrap(uUNIT);
/// @dev The unit number squared.
uint256 constant uUNIT_SQUARED = 1e36;
UD60x18 constant UNIT_SQUARED = UD60x18.wrap(uUNIT_SQUARED);
/// @dev Zero as a UD60x18 number.
UD60x18 constant ZERO = UD60x18.wrap(0);
"
},
"lib/prb-math/src/ud60x18/Conversions.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { uMAX_UD60x18, uUNIT } from "./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from "./Errors.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function convert(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x) / uUNIT;
}
/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UD60x18 / UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to UD60x18.
function convert(uint256 x) pure returns (UD60x18 result) {
if (x > uMAX_UD60x18 / uUNIT) {
revert PRBMath_UD60x18_Convert_Overflow(x);
}
unchecked {
result = UD60x18.wrap(x * uUNIT);
}
}
"
},
"lib/prb-math/src/ud60x18/Errors.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD60x18 } from "./ValueType.sol";
/// @notice Thrown when ceiling a number overflows UD60x18.
error PRBMath_UD60x18_Ceil_Overflow(UD60x18 x);
/// @notice Thrown when converting a basic integer to the fixed-point format overflows UD60x18.
error PRBMath_UD60x18_Convert_Overflow(uint256 x);
/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_UD60x18_Exp_InputTooBig(UD60x18 x);
/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_UD60x18_Exp2_InputTooBig(UD60x18 x);
/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows UD60x18.
error PRBMath_UD60x18_Gm_Overflow(UD60x18 x, UD60x18 y);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_UD60x18_IntoSD1x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD59x18.
error PRBMath_UD60x18_IntoSD59x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_UD60x18_IntoUD2x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_UD60x18_IntoUint128_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_UD60x18_IntoUint40_Overflow(UD60x18 x);
/// @notice Thrown when taking the logarithm of a number less than 1.
error PRBMath_UD60x18_Log_InputTooSmall(UD60x18 x);
/// @notice Thrown when calculating the square root overflows UD60x18.
error PRBMath_UD60x18_Sqrt_Overflow(UD60x18 x);
"
},
"lib/prb-math/src/ud60x18/Helpers.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { wrap } from "./Casting.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Implements the checked addition operation (+) in the UD60x18 type.
function add(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() + y.unwrap());
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() & bits);
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and2(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() & y.unwrap());
}
/// @notice Implements the equal operation (==) in the UD60x18 type.
function eq(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() == y.unwrap();
}
/// @notice Implements the greater than operation (>) in the UD60x18 type.
function gt(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() > y.unwrap();
}
/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.
function gte(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() >= y.unwrap();
}
/// @notice Implements a zero comparison check function in the UD60x18 type.
function isZero(UD60x18 x) pure returns (bool result) {
// This wouldn't work if x could be negative.
result = x.unwrap() == 0;
}
/// @notice Implements the left shift operation (<<) in the UD60x18 type.
function lshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() << bits);
}
/// @notice Implements the lower than operation (<) in the UD60x18 type.
function lt(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() < y.unwrap();
}
/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.
function lte(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() <= y.unwrap();
}
/// @notice Implements the checked modulo operation (%) in the UD60x18 type.
function mod(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() % y.unwrap());
}
/// @notice Implements the not equal operation (!=) in the UD60x18 type.
function neq(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() != y.unwrap();
}
/// @notice Implements the NOT (~) bitwise operation in the UD60x18 type.
function not(UD60x18 x) pure returns (UD60x18 result) {
result = wrap(~x.unwrap());
}
/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.
function or(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() | y.unwrap());
}
/// @notice Implements the right shift operation (>>) in the UD60x18 type.
function rshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() >> bits);
}
/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.
function sub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() - y.unwrap());
}
/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.
function uncheckedAdd(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() + y.unwrap());
}
}
/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.
function uncheckedSub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() - y.unwrap());
}
}
/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.
function xor(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() ^ y.unwrap());
}
"
},
"lib/prb-math/src/ud60x18/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
uEXP_MAX_INPUT,
uEXP2_MAX_INPUT,
uHALF_UNIT,
uLOG2_10,
uLOG2_E,
uMAX_UD60x18,
uMAX_WHOLE_UD60x18,
UNIT,
uUNIT,
uUNIT_SQUARED,
ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
///
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
unchecked {
result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
}
}
/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_UD60x18`.
///
/// @param x The UD60x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint > uMAX_WHOLE_UD60x18) {
revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
}
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `UNIT - remainder`.
let delta := sub(uUNIT, remainder)
// Equivalent to `x + remainder > 0 ? delta : 0`.
result := add(x, mul(delta, gt(remainder, 0)))
}
}
/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @param result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}
/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// This check prevents values greater than 192e18 from being passed to {exp2}.
if (xUint > uEXP_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
}
unchecked {
// Inline the fixed-point multiplication to save gas.
uint256 doubleUnitProduct = xUint * uLOG2_E;
result = exp2(wrap(doubleUnitProduct / uUNIT));
}
}
/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
if (xUint > uEXP2_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
}
// Convert x to the 192.64-bit fixed-point format.
uint256 x_192x64 = (xUint << 64) / uUNIT;
// Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
result = wrap(Common.exp2(x_192x64));
}
/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `x - remainder > 0 ? remainder : 0)`.
result := sub(x, mul(remainder, gt(remainder, 0)))
}
}
/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @param result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
result := mod(x, uUNIT)
}
}
/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
if (xUint == 0 || yUint == 0) {
return ZERO;
}
unchecked {
// Checking for overflow this way is faster than letting Solidity do it.
uint256 xyUint = xUint * yUint;
if (xyUint / xUint != yUint) {
revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
}
// We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
// during multiplication. See the comments in {Common.sqrt}.
result = wrap(Common.sqrt(xyUint));
}
}
/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
result = wrap(uUNIT_SQUARED / x.unwrap());
}
}
/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
// Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
// {log2} can return is ~196_205294292027477728.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}
}
/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
// Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
// prettier-ignore
assembly ("memory-safe") {
switch x
case 1 { result := mul(uUNIT, sub(0, 18)) }
case 10 { result := mul(uUNIT, sub(1, 18)) }
case 100 { result := mul(uUNIT, sub(2, 18)) }
case 1000 { result := mul(uUNIT, sub(3, 18)) }
case 10000 { result := mul(uUNIT, sub(4, 18)) }
case 100000 { result := mul(uUNIT, sub(5, 18)) }
case 1000000 { result := mul(uUNIT, sub(6, 18)) }
case 10000000 { result := mul(uUNIT, sub(7, 18)) }
case 100000000 { result := mul(uUNIT, sub(8, 18)) }
case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
case 1000000000000000000 { result := 0 }
case 10000000000000000000 { result := uUNIT }
case 100000000000000000000 { result := mul(uUNIT, 2) }
case 1000000000000000000000 { result := mul(uUNIT, 3) }
case 10000000000000000000000 { result := mul(uUNIT, 4) }
case 100000000000000000000000 { result := mul(uUNIT, 5) }
case 1000000000000000000000000 { result := mul(uUNIT, 6) }
case 10000000000000000000000000 { result := mul(uUNIT, 7) }
case 100000000000000000000000000 { result := mul(uUNIT, 8) }
case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
default { result := uMAX_UD60x18 }
}
if (result.unwrap() == uMAX_UD60x18) {
unchecked {
// Inline the fixed-point division to save gas.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
}
}
}
/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \
Submitted on: 2025-09-19 10:45:34
Comments
Log in to comment.
No comments yet.