Address Contract Verified
Address
0x295eD2f7537883fDE8f5bf55cb6696a4a4Dbc70e
Balance
0 ETH
Nonce
1
Code Size
4689 bytes
Creator
0x4C0455f5...7582 at tx 0x15f9dd77...370b3d
Indexed Transactions
Index loading...
Contract Bytecode
4689 bytes
0x6080604081815260049182361015610015575f80fd5b5f3560e01c9081631e99d56914610742575080632f45cd6f14610724578063379607f51461070657806364d60d9114610661578063690d450a146104b1578063773928c3146103ec57806398e36d8b146103a6578063d0597f5c146101335763e826649114610082575f80fd5b3461012f5761016036600319011261012f578051916100a08361075b565b6001600160a01b039035818116810361012f578352602435818116810361012f576020840152604435908116810361012f5782610128918360209501526064356060820152608435608082015260a43560a082015260c43560c082015260e43560e0820152610104356101008201526101243561012082015261014435610140820152610b6e565b9051908152f35b5f80fd5b50903461012f5760208060031936011261012f57813592835f5260018252805f208151906101608261075b565b60018060a01b038082541683528060018301541694808401958652816002840154169085850191825260038401546060860195818752610140600a8b8801549760808401988952600581015460a0850152600681015460c0850152600781015460e08501526008810154610100850152600981015461012085015201549101521561038f57828751163381036103625750885f526001815261023f865f20600a5f918281558260018201558260028201558260038201558260048201558260058201558260068201558260078201558260088201558260098201550155565b6102908184845116858a51169061025989518951906107de565b8a5163a9059cbb60e01b81526001600160a01b03909316838e0190815260208101919091529193849283915f918391604090910190565b03925af1918215610358575f9261032b575b5050156102d15787337ffd131e2ea3c730111e1fcb8c577a2abf7c2c4857bc6a009e4ebf63a4ebeec96f5f80a3005b91816102ec92610327969594511696511692519051906107de565b915163cd3f165960e01b81526001600160a01b0394851695810195865230602087015293166040850152606084015290918291608090910190565b0390fd5b61034a9250803d10610351575b61034281836107bc565b8101906107ff565b5f806102a2565b503d610338565b87513d5f823e3d90fd5b8651637667c0fd60e11b815233818b019081526001600160a01b0390921660208301529081906040010390fd5b85516347e7ef2160e11b81528089018a9052602490fd5b50903461012f57602036600319011261012f57355f9081526002602090815290829020805460019091015492516001600160a01b03909116815290810191909152604090f35b503461012f573660031901610200811261012f576101801361012f576101e4359160ff831680930361012f576044356001600160a01b038116939084900361012f57833b1561012f5760e45f92838551968794859363d505accf60e01b85523390850152306024850152606435604485015261018435606485015260848401526101a43560a48401526101c43560c48401525af19182156104a757602092610498575b50610128610cbf565b6104a19061078c565b5f61048f565b50513d5f823e3d90fd5b503461012f57602036600319011261012f57813591825f526001602052815f208251926104dd8461075b565b60018060a01b0393848354168152846001840154169060208101918252856002850154168382019081526003850154956060830187815281870154906080850191825260058801549360a0860194855260068901549560c0810196875260078a01549760e0820198895260088b01549961010083019a8b52600a60098d01549c61012085019d8e5201549c61014084019d8e521561064b578d82511633810361061e5750908d8f9281935f84525f5260016020525f209e8f945116936001600160601b0360a01b94858254161781556001019151168382541617905560028d01925116908254161790555160038a015551908801555160058701555160068601555160078501555160088401555160098301555190600a015533907f55214fa39e50551eeabc217e58c461d49ee9d96608b1930a06b6cb5aba5e189f5f80a3005b8151637667c0fd60e11b815233818a019081526001600160a01b0390921660208301529081906040010390fd5b516347e7ef2160e11b81528087018f9052602490fd5b50903461012f57602036600319011261012f576101609181355f526001602052805f2060018060a01b03918282541693836001840154169360028401541690600384015490840154600585015490600686015492600787015494600888015496600a60098a01549901549981519b8c5260208c01528a01526060890152608088015260a087015260c086015260e0850152610100840152610120830152610140820152f35b823461012f57602036600319011261012f576107229035610817565b005b503461012f5761018036600319011261012f57602090610128610cbf565b3461012f575f36600319011261012f576020905f548152f35b610160810190811067ffffffffffffffff82111761077857604052565b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff811161077857604052565b6040810190811067ffffffffffffffff82111761077857604052565b90601f8019910116810190811067ffffffffffffffff82111761077857604052565b919082039182116107eb57565b634e487b7160e01b5f52601160045260245ffd5b9081602091031261012f5751801515810361012f5790565b805f52600160205260405f20600a604051916108328361075b565b80546001600160a01b03908116845260018201548116602085015260028201541660408401526003810154606084015260048101546080840152600581015460a0840152600681015460c0840152600781015460e08401526008810154610100840152600981015461012084015201546101408201526108b2818361111b565b90606081015115610b17576108c681610b6e565b918215610b11576108db6080830151846107de565b90836080840152845f52600160205260405f2060018060a01b038451166001600160601b0360a01b90818354161782556001820160018060a01b0360208701511682825416179055600282019060018060a01b0360408701511690825416179055606084015160038201556080840151600482015560a0840151600582015560c0840151600682015560e0840151600782015561010084015160088201556101208401516009820155600a6101408501519101558115610af857604083810151905163a9059cbb60e01b81526001600160a01b038381166004830152602482018590529091602091839160449183915f91165af1908115610aed575f91610ace575b5015610a95575090836060926040519081527ff0c7417137ff1580f5ffd60b1cc0f9afe8b20bebeeafbb2b8c0f9a4cf600d59960203392a3015114610a1f5750565b805f526001602052610a6f60405f20600a5f918281558260018201558260028201558260038201558260048201558260058201558260068201558260078201558260088201558260098201550155565b7f7f04582985ec7e9b374f3597082a46b8833186436ea793a2d58a372eae0aba845f80a2565b604092830151925163cd3f165960e01b81526001600160a01b039384166004820152306024820152921660448301526064820152608490fd5b610ae7915060203d6020116103515761034281836107bc565b5f6109dd565b6040513d5f823e3d90fd5b6040516346d8ea7760e01b815260048101869052602490fd5b50505050565b6040516347e7ef2160e11b815260048101849052602490fd5b919082018092116107eb57565b818102929181159184041417156107eb57565b8115610b5a570490565b634e487b7160e01b5f52601260045260245ffd5b60a08101514210610c9b5760e08101908151421015610c9257806060610bc292015190610ba0610100820151836111ed565b928392610bbd610bb5610120850151836111ed565b9586926107de565b6107de565b9260c082018051804210610c885761014091610be1610beb92426107de565b97519051906107de565b920194855115610c73578551610c0091610b50565b9185518015610b5a578106610c445791610c30610c3692610c2a610c3b9695610c41995190610b50565b94610b30565b94610b3d565b610b50565b90610b30565b90565b610c52919392955190610b50565b91600183018093116107eb57610c4194610c30610c3692610c3b9594610b30565b610c419550610c30610c3692610c3b95610b30565b5050505091505090565b60609150015190565b505f90565b9081602091031261012f57516001600160a01b038116810361012f5790565b5f545f1981146107eb57600181015f5560643580156111095760a43560843581811180156110fe575b6110ec576004926001600160a01b039084358281169081900361012f57801561100f575b604435948386169485870361012f57604080516370a0823160e01b808252308b830152919860209687836024818d5afa928315611005578a8d8d8b935f97610fd0575b505f9392916064915194859384926323b872dd60e01b845233908401523060248401528760448401525af1908115610fc6575f91610fa9575b5015610f6f5750508851918252308a83015285826024818b5afa8015610f65575f90610f36575b610db992506107de565b956024359086821680920361012f5760e4359361010435938a5196610ddd8861075b565b87528787019384528a870192835260608701998a5260808701995f8b5260a0880191825260c0880192835260e088019360c4358552610100890195888752670de0b6b3a7640000610e416101208c0199808b526101408d019b610124358d52610b30565b11610f26578f8e8d915f5260018d525f209a51169b6001600160601b0360a01b918d838d5416178c558160018d019151168382541617905560028b019251169082541617905551998a6003890155518c8801555160058701555160068601555160078501555160088401555160098301555190600a0155835186815233917fbab990fbc4c40ebfc84ac492619d9ed676eba7ba9a01bf0b9ea05c3fa61f9a4191a4303b1561012f57805163379607f560e01b81529182018390525f8260248183305af1908115610f1d5750610f14575090565b610c419061078c565b513d5f823e3d90fd5b8d51631b742d9d60e31b81528f90fd5b508582813d8311610f5e575b610f4c81836107bc565b8101031261012f57610db99151610daf565b503d610f42565b89513d5f823e3d90fd5b8a5163cd3f165960e01b81526001600160a01b03909216828d01908152336020820152306040820152606081019190915281906080010390fd5b610fc09150893d8b116103515761034281836107bc565b5f610d88565b8c513d5f823e3d90fd5b965050505083813d8311610ffe575b610fe981836107bc565b8101031261012f57915191878a8d8d5f610d4f565b503d610fdf565b8b513d5f823e3d90fd5b6101443583811680910361012f5780156110db57610164356040516331a9108f60e11b81528189820152602081602481865afa8015610aed5786915f916110ac575b50161561109b5760019060405192611068846107a0565b835260208301908152895f5260026020528560405f209351166001600160601b0360a01b84541617835551910155610d0c565b604051634e46966960e11b81528890fd5b6110ce915060203d6020116110d4575b6110c681836107bc565b810190610ca0565b5f611051565b503d6110bc565b604051634e46966960e11b81528790fd5b604051631b742d9d60e31b8152600490fd5b5060c4358211610ce8565b60405163162908e360e11b8152600490fd5b90516001600160a01b0391908216806111e757505f5260026020526040805f20918151611147816107a0565b6001828554169485835201549360208201948552156111d657816020915116935160248451809681936331a9108f60e11b835260048301525afa9283156111cc575f936111ab575b5082161561119b575090565b51634e46966960e11b8152600490fd5b6111c591935060203d6020116110d4576110c681836107bc565b915f61118f565b82513d5f823e3d90fd5b8251634e46966960e11b8152600490fd5b91505090565b90805f19048211611208575b670de0b6b3a764000091020490565b80156111f95763bac65e5b5f526004601cfdfea2646970667358221220b90fe22908aed06c8947140f5b71769e4a336a10c03dc97e1458a13ab140056964736f6c63430008190033
Verified Source Code Full Match
Compiler: v0.8.25+commit.b61c2a91
EVM: cancun
Optimization: Yes (200 runs)
CliqueLock.sol 336 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {ICliqueLock, StreamConfig, Stream, Badge, IVerifier} from "./ICliqueLock.sol";
/**
* @title CliqueLock
* @notice A flexible token vesting/streaming contract that supports various vesting schedules
* @dev This contract enables creation of token streams with customizable parameters including:
* - Initial unlock percentage at start
* - Cliff unlock percentage
* - Linear or piece-wise vesting after cliff
* - Revocable streams with designated authority
*
* The vesting schedule consists of three main phases:
* 1. Initial Unlock: Immediate unlock at stream start (startUnlockPercentage)
* 2. Cliff Period: Additional unlock at cliff time (cliffUnlockPercentage)
* 3. Vesting Period: Remaining tokens vest either:
* - Linearly (when pieceDuration = 0)
* - In discrete pieces (when pieceDuration > 0)
*
* Key Features:
* - Supports both standard ERC20 and ERC20Permit tokens
* - Handles tokens with transfer fees correctly
* - Allows stream revocation by designated authority
* - Flexible claiming mechanism
* - Safe math operations using FixedPointMathLib
*
* Security Features:
* - Reentrancy protection through checks-effects-interactions pattern
* - Precise arithmetic using fixed-point math
* - Validation of vesting schedule parameters
*/
contract CliqueLock {
using FixedPointMathLib for uint256;
/// @notice Counter for generating unique stream IDs
uint256 public nextStreamId;
/// @notice Mapping from stream ID to Stream struct
mapping(uint256 => Stream) public streams;
/// @notice Mapping from stream ID to Badge struct
mapping(uint256 => Badge) public badges;
/// @notice Thrown when stream schedule parameters are invalid
error InvalidSchedule();
/// @notice Thrown when caller is not the authorized revoker
/// @param caller Address attempting to revoke
/// @param authority Address authorized to revoke
error InvalidRevokeAuthority(address caller, address authority);
/// @notice Thrown when stream ID does not exist
/// @param streamId ID of the non-existent stream
error StreamNotFound(uint256 streamId);
/// @notice Thrown when token transfer fails
/// @param token Address of the token
/// @param from Source address
/// @param to Destination address
/// @param amount Amount being transferred
error TransferFailed(address token, address from, address to, uint256 amount);
/// @notice Thrown when stream has no remaining tokens to claim
/// @param streamId ID of the stream
error StreamConsumed(uint256 streamId);
/// @notice Thrown when recipient is not a valid
error InvalidRecipient();
/// @notice Thrown when stream amount is 0
error InvalidAmount();
/// @notice Emitted when a new stream is created
/// @param creator Address creating the stream
/// @param beneficiary Address receiving the stream
/// @param amount Total amount of tokens in the stream
/// @param streamId Unique identifier of the created stream
event StreamCreated(address indexed creator, address indexed beneficiary, uint256 indexed amount, uint256 streamId);
/// @notice Emitted when a stream is revoked
/// @param caller Address revoking the stream
/// @param streamId ID of the revoked stream
event StreamRevoked(address indexed caller, uint256 indexed streamId);
/// @notice Emitted when tokens are claimed from a stream
/// @param claimer Address claiming the tokens
/// @param streamId ID of the stream being claimed from
/// @param amount Amount of tokens claimed
event StreamClaimed(address indexed claimer, uint256 indexed streamId, uint256 amount);
/// @notice Emitted when a stream's revoke authority is removed
/// @param streamId ID of the stream
/// @param revokeAuthority Address of the removed authority
event RevokeAuthorityRemoved(uint256 indexed streamId, address indexed revokeAuthority);
/// @notice Emitted when a stream ends
/// @param streamId ID of the stream
event StreamEnd(uint256 indexed streamId);
// EXTERNAL FUNCTIONS - STREAM CREATION
/**
* @notice Creates a stream using standard ERC20 approve
* @dev Requires prior approval of token transfer
* @param config StreamConfig struct containing the stream parameters
* @return streamId Unique identifier of the created stream
*/
function createStream(StreamConfig calldata config) external returns (uint256 streamId) {
return _createStream(config);
}
/**
* @notice Creates a stream using ERC20Permit, avoiding a separate approve transaction
* @dev Uses EIP-2612 permit to approve token transfer and creates stream
* @param config StreamConfig struct containing the stream parameters
* @param deadline Timestamp until which the permit is valid
* @param r R component of the permit signature
* @param s S component of the permit signature
* @param v V component of the permit signature
* @return streamId Unique identifier of the created stream
*/
function createStreamWithPermit(StreamConfig calldata config, uint256 deadline, bytes32 r, bytes32 s, uint8 v)
external
returns (uint256 streamId)
{
IERC20Permit(config.token).permit(msg.sender, address(this), config.amount, deadline, v, r, s);
return _createStream(config);
}
// EXTERNAL FUNCTIONS - STREAM MANAGEMENT
/**
* @notice Claims available tokens from a stream
* @dev Transfers all currently claimable tokens to the recipient
* @param streamId ID of the stream to claim from
*/
function claim(uint256 streamId) external {
Stream memory stream = streams[streamId];
address recipient = _resolveRecipient(streamId, stream);
if (stream.amount == 0) {
revert StreamNotFound(streamId);
}
uint256 claimableAmount = calculateClaimableAmount(stream);
if (claimableAmount == 0) {
return;
}
uint256 amount = claimableAmount - stream.claimedAmount;
stream.claimedAmount = claimableAmount;
streams[streamId] = stream;
if (amount == 0) {
revert StreamConsumed(streamId);
}
if (!IERC20(stream.token).transfer(recipient, amount)) {
revert TransferFailed(stream.token, address(this), recipient, amount);
}
emit StreamClaimed(msg.sender, streamId, amount);
if (claimableAmount == stream.amount) {
delete streams[streamId];
emit StreamEnd(streamId);
}
}
/**
* @notice Revokes a stream, returning unclaimed tokens to the revokeAuthority
* @dev Can only be called by the designated revokeAuthority
* @param streamId ID of the stream to revoke
*/
function revokeStream(uint256 streamId) external {
Stream memory stream = streams[streamId];
if (stream.amount == 0) {
revert StreamNotFound(streamId);
}
if (stream.revokeAuthority != msg.sender) {
revert InvalidRevokeAuthority(msg.sender, stream.revokeAuthority);
}
delete streams[streamId];
if (!IERC20(stream.token).transfer(stream.revokeAuthority, stream.amount - stream.claimedAmount)) {
revert TransferFailed(
stream.token, address(this), stream.revokeAuthority, stream.amount - stream.claimedAmount
);
}
emit StreamRevoked(msg.sender, streamId);
}
/**
* @notice Removes the revoke authority from a stream
* @dev Can only be called by the current revoke authority
* @param streamId ID of the stream to remove authority from
*/
function removeRevokeAuthority(uint256 streamId) external {
Stream memory stream = streams[streamId];
if (stream.amount == 0) {
revert StreamNotFound(streamId);
}
if (stream.revokeAuthority != msg.sender) {
revert InvalidRevokeAuthority(msg.sender, stream.revokeAuthority);
}
stream.revokeAuthority = address(0);
streams[streamId] = stream;
emit RevokeAuthorityRemoved(streamId, msg.sender);
}
// PUBLIC VIEW FUNCTIONS
/**
* @notice Calculates the amount of tokens currently claimable from a stream
* @dev Implements the vesting schedule logic
* @param stream Stream struct containing the stream parameters
* @return Amount of tokens that can be claimed
*/
function calculateClaimableAmount(Stream memory stream) public view returns (uint256) {
/// t \in [0, startTime)
if (block.timestamp < stream.startTime) {
return 0;
}
/// t \in [endTime, infinity)
if (block.timestamp >= stream.endTime) {
return stream.amount;
}
uint256 totalAmount = stream.amount;
uint256 startUnlockAmount = totalAmount.mulWad(stream.startUnlockPercentage);
uint256 cliffUnlockAmount = totalAmount.mulWad(stream.cliffUnlockPercentage);
uint256 vestedAmount = totalAmount - startUnlockAmount - cliffUnlockAmount;
/// t \in [startTime, cliffTime)
if (block.timestamp < stream.cliffTime) {
return startUnlockAmount;
}
/// t \in [cliffTime, endTime)
uint256 elapsedTime = block.timestamp - stream.cliffTime;
uint256 vestingDuration = stream.endTime - stream.cliffTime;
/// if skipTime is 0, vesting is linear
if (stream.pieceDuration == 0) {
return startUnlockAmount + cliffUnlockAmount + vestedAmount * elapsedTime / vestingDuration;
}
/// if skipTime is not 0, vesting is piecewised
uint256 pieces = elapsedTime / stream.pieceDuration;
uint256 totalPieces;
if (vestingDuration % stream.pieceDuration == 0) {
totalPieces = vestingDuration / stream.pieceDuration;
} else {
totalPieces = vestingDuration / stream.pieceDuration + 1;
}
return startUnlockAmount + cliffUnlockAmount + vestedAmount * pieces / totalPieces;
}
// INTERNAL FUNCTIONS
/**
* @notice Internal function to create a new stream
* @dev Handles the actual stream creation logic, including token transfer and validation
* @param config StreamConfig struct containing the stream parameters
* @return ID of the created stream
*/
function _createStream(StreamConfig calldata config) internal returns (uint256) {
uint256 streamId = nextStreamId++;
if (config.amount == 0) {
revert InvalidAmount();
}
if (config.startTime > config.cliffTime || config.cliffTime > config.endTime) {
revert InvalidSchedule();
}
if (config.recipient == address(0)) {
if (config.verifier == address(0)) {
revert InvalidRecipient();
}
if (IVerifier(config.verifier).ownerOf(config.id) == address(0)) {
revert InvalidRecipient();
}
Badge memory badge = Badge({verifier: config.verifier, id: config.id});
badges[streamId] = badge;
}
// In case of ERC20 with fee, the actual amount transferred is less than the amount approved.
uint256 balanceBefore = IERC20(config.token).balanceOf(address(this));
if (!IERC20(config.token).transferFrom(msg.sender, address(this), config.amount)) {
revert TransferFailed(config.token, msg.sender, address(this), config.amount);
}
uint256 balanceAfter = IERC20(config.token).balanceOf(address(this));
uint256 actualAmount = balanceAfter - balanceBefore;
Stream memory stream = Stream({
recipient: config.recipient,
revokeAuthority: config.revokeAuthority,
token: config.token,
amount: actualAmount,
claimedAmount: 0,
startTime: config.startTime,
cliffTime: config.cliffTime,
endTime: config.endTime,
startUnlockPercentage: config.startUnlockPercentage,
cliffUnlockPercentage: config.cliffUnlockPercentage,
pieceDuration: config.pieceDuration
});
if (stream.startUnlockPercentage + stream.cliffUnlockPercentage > 1e18) {
revert InvalidSchedule();
}
streams[streamId] = stream;
emit StreamCreated(msg.sender, stream.recipient, stream.amount, streamId);
this.claim(streamId);
return streamId;
}
/// @notice Resolves the recipient address for a stream
/// @param streamId ID of the stream
/// @param stream Stream struct containing the stream parameters
/// @return Address of the recipient
function _resolveRecipient(uint256 streamId, Stream memory stream) internal view returns (address) {
if (stream.recipient != address(0)) {
return stream.recipient;
}
Badge memory badge = badges[streamId];
if (badge.verifier == address(0)) {
revert InvalidRecipient();
}
address owner = IVerifier(badge.verifier).ownerOf(badge.id);
if (owner == address(0)) {
revert InvalidRecipient();
}
return owner;
}
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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);
}
FixedPointMathLib.sol 1302 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error ExpOverflow();
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error FactorialOverflow();
/// @dev The operation failed, due to an overflow.
error RPowOverflow();
/// @dev The mantissa is too big to fit.
error MantissaOverflow();
/// @dev The operation failed, due to an multiplication overflow.
error MulWadFailed();
/// @dev The operation failed, due to an multiplication overflow.
error SMulWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error DivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error SDivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @dev The division failed, as the denominator is zero.
error DivFailed();
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @dev The output is undefined, as the input is less-than-or-equal to zero.
error LnWadUndefined();
/// @dev The input outside the acceptable domain.
error OutOfDomain();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The scalar of ETH and most ERC20s.
uint256 internal constant WAD = 1e18;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIMPLIFIED FIXED POINT OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if gt(x, div(not(0), y)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up.
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if iszero(eq(div(z, y), x)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, WAD)
// Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
if iszero(mul(y, eq(sdiv(z, WAD), x))) {
mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up.
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
/// Note: This function is an approximation.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}
/// @dev Returns `exp(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is less than 0.5 we return zero.
// This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
if (x <= -41446531673892822313) return r;
/// @solidity memory-safe-assembly
assembly {
// When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
// an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
if iszero(slt(x, 135305999368893231589)) {
mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
revert(0x1c, 0x04)
}
}
// `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
x = x - k * 54916777467707473351141471128;
// `k` is in the range `[-61, 195]`.
// Evaluate using a (6, 7)-term rational approximation.
// `p` is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already `2**96` too large.
r := sdiv(p, q)
}
// r should be in the range `(0.09, 0.25) * 2**96`.
// We now need to multiply r by:
// - The scale factor `s ≈ 6.031367120`.
// - The `2**k` factor from the range reduction.
// - The `1e18 / 2**96` factor for base conversion.
// We do this all at once, with an intermediate result in `2**213`
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
);
}
}
/// @dev Returns `ln(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function lnWad(int256 x) internal pure returns (int256 r) {
/// @solidity memory-safe-assembly
assembly {
// We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
// We do this by multiplying by `2**96 / 10**18`. But since
// `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
// and add `ln(2**96 / 10**18)` at the end.
// Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// We place the check here for more optimal stack operations.
if iszero(sgt(x, 0)) {
mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
revert(0x1c, 0x04)
}
// forgefmt: disable-next-item
r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
x := shr(159, shl(r, x))
// Evaluate using a (8, 8)-term rational approximation.
// `p` is made monic, we will multiply by a scale factor later.
// forgefmt: disable-next-item
let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
sar(96, mul(add(43456485725739037958740375743393,
sar(96, mul(add(24828157081833163892658089445524,
sar(96, mul(add(3273285459638523848632254066296,
x), x))), x))), x)), 11111509109440967052023855526967)
p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
// `q` is monic by convention.
let q := add(5573035233440673466300451813936, x)
q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
q := add(909429971244387300277376558375, sar(96, mul(x, q)))
// `p / q` is in the range `(0, 0.125) * 2**96`.
// Finalization, we need to:
// - Multiply by the scale factor `s = 5.549…`.
// - Add `ln(2**96 / 10**18)`.
// - Add `k * ln(2)`.
// - Multiply by `10**18 / 2**96 = 5**18 >> 78`.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already `2**96` too large.
p := sdiv(p, q)
// Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
p := mul(1677202110996718588342820967067443963516166, p)
// Add `ln(2) * k * 5**18 * 2**192`.
// forgefmt: disable-next-item
p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
// Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
// Base conversion: mul `2**18 / 2**192`.
r := sar(174, p)
}
}
/// @dev Returns `W_0(x)`, denominated in `WAD`.
/// See: https://en.wikipedia.org/wiki/Lambert_W_function
/// a.k.a. Product log function. This is an approximation of the principal branch.
/// Note: This function is an approximation. Monotonically increasing.
function lambertW0Wad(int256 x) internal pure returns (int256 w) {
// forgefmt: disable-next-item
unchecked {
if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
(int256 wad, int256 p) = (int256(WAD), x);
uint256 c; // Whether we need to avoid catastrophic cancellation.
uint256 i = 4; // Number of iterations.
if (w <= 0x1ffffffffffff) {
if (-0x4000000000000 <= w) {
i = 1; // Inputs near zero only take one step to converge.
} else if (w <= -0x3ffffffffffffff) {
i = 32; // Inputs near `-1/e` take very long to converge.
}
} else if (uint256(w >> 63) == uint256(0)) {
/// @solidity memory-safe-assembly
assembly {
// Inline log2 for more performance, since the range is small.
let v := shr(49, w)
let l := shl(3, lt(0xff, v))
l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
c := gt(l, 60)
i := add(2, add(gt(l, 53), c))
}
} else {
int256 ll = lnWad(w = lnWad(w));
/// @solidity memory-safe-assembly
assembly {
// `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
i := add(3, iszero(shr(68, x)))
c := iszero(shr(143, x))
}
if (c == uint256(0)) {
do { // If `x` is big, use Newton's so that intermediate values won't overflow.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := mul(w, div(e, wad))
w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
}
if (p <= w) break;
p = w;
} while (--i != uint256(0));
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
return w;
}
}
do { // Otherwise, use Halley's for faster convergence.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := add(w, wad)
let s := sub(mul(w, e), mul(x, wad))
w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
}
if (p <= w) break;
p = w;
} while (--i != c);
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
// For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
// R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
if (c == uint256(0)) return w;
int256 t = w | 1;
/// @solidity memory-safe-assembly
assembly {
x := sdiv(mul(x, wad), t)
}
x = (t * (wad + lnWad(x)));
/// @solidity memory-safe-assembly
assembly {
w := sdiv(x, add(wad, t))
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a * b == x * y`, with full precision.
function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// 512-bit multiply `[p1 p0] = x * y`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = p1 * 2**256 + p0`.
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`.
for {} 1 {} {
// If overflows.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
/*------------------- 512 by 256 division --------------------*/
// Make division exact by subtracting the remainder from `[p1 p0]`.
let r := mulmod(x, y, d) // Compute remainder using mulmod.
let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
// Make sure `z` is less than `2**256`. Also prevents `d == 0`.
// Placing the check here seems to give more optimal stack operations.
if iszero(gt(d, p1)) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
d := div(d, t) // Divide `d` by `t`, which is a power of two.
// Invert `d mod 2**256`
// Now that `d` is an odd number, it has an inverse
// modulo `2**256` such that `d * inv = 1 mod 2**256`.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, `d * inv = 1 mod 2**4`.
let inv := xor(2, mul(3, d))
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
z :=
mul(
// Divide [p1 p0] by the factors of two.
// Shift in bits from `p1` into `p0`. For this we need
// to flip `t` such that it is `2**256 / t`.
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
)
break
}
z := div(z, d)
break
}
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
/// Performs the full 512 bit calculation regardless.
function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z)))
let t := and(d, sub(0, d))
let r := mulmod(x, y, d)
d := div(d, t)
let inv := xor(2, mul(3, d))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
z :=
mul(
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv)
)
}
}
/// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Uniswap-v3-core under MIT license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
z = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
z := add(z, 1)
if iszero(z) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
/// Throws if result overflows a uint256.
/// Credit to Philogy under MIT license:
/// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
for {} 1 {} {
if iszero(or(iszero(x), eq(div(z, x), y))) {
let k := and(n, 0xff) // `n`, cleaned.
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
// | p1 | z |
// Before: | p1_0 ¦ p1_1 | z_0 ¦ z_1 |
// Final: | 0 ¦ p1_0 | p1_1 ¦ z_0 |
// Check that final `z` doesn't overflow by checking that p1_0 = 0.
if iszero(shr(k, p1)) {
z := add(shl(sub(256, k), p1), shr(k, z))
break
}
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
z := shr(and(n, 0xff), z)
break
}
}
}
/// @dev Returns `floor(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := div(z, d)
}
}
/// @dev Returns `ceil(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(z, d))), div(z, d))
}
}
/// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
/// @solidity memory-safe-assembly
assembly {
let g := n
let r := mod(a, n)
for { let y := 1 } 1 {} {
let q := div(g, r)
let t := g
g := r
r := sub(t, mul(r, q))
let u := x
x := y
y := sub(u, mul(y, q))
if iszero(r) { break }
}
x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
}
}
/// @dev Returns `ceil(x / d)`.
/// Reverts if `d` is zero.
function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
if iszero(d) {
mstore(0x00, 0x65244e4e) // `DivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(x, d))), div(x, d))
}
}
/// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `max(0, x - y)`.
function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x + y)`.
function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(0, lt(add(x, y), x)), add(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x * y)`.
function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `x != 0 ? x : y`, without branching.
function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != bytes32(0) ? x : y`, without branching.
function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != address(0) ? x : y`, without branching.
function coalesce(address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(shl(96, x))))
}
}
/// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
/// Reverts if the computation overflows.
function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
if x {
z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
let half := shr(1, b) // Divide `b` by 2.
// Divide `y` by 2 every iteration.
for { y := shr(1, y) } y { y := shr(1, y) } {
let xx := mul(x, x) // Store x squared.
let xxRound := add(xx, half) // Round to the nearest number.
// Revert if `xx + half` overflowed, or if `x ** 2` overflows.
if or(lt(xxRound, xx), shr(128, x)) {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
x := div(xxRound, b) // Set `x` to scaled `xxRound`.
// If `y` is odd:
if and(y, 1) {
let zx := mul(z, x) // Compute `z * x`.
let zxRound := add(zx, half) // Round to the nearest number.
// If `z * x` overflowed or `zx + half` overflowed:
if or(xor(div(zx, x), z), lt(zxRound, zx)) {
// Revert if `x` is non-zero.
if x {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
}
z := div(zxRound, b) // Return properly scaled `zxRound`.
}
}
}
}
}
/// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
// but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffffff, shr(r, x))))
z := shl(shr(1, r), z)
// Goal was to get `z*z*y` within a small factor of `x`. More iterations could
// get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
// We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
// That's not possible if `x < 256` but we can just verify those cases exhaustively.
// Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
// Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
// Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
// For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
// is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
// with largest error when `s = 1` and when `s = 256` or `1/256`.
// Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
// Then we can estimate `sqrt(y)` using
// `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
// There is no overflow risk here since `y < 2**136` after the first branch above.
z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If `x+1` is a perfect square, the Babylonian method cycles between
// `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
z := sub(z, lt(div(x, z), z))
}
}
/// @dev Returns the cube root of `x`, rounded down.
/// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
/// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// Makeshift lookup table to nudge the approximate log2 result.
z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
// Newton-Raphson's.
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
// Round down.
z := sub(z, lt(div(x, mul(z, z)), z))
}
}
/// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
function sqrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
z = (1 + sqrt(x)) * 10 ** 9;
z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
}
/// @solidity memory-safe-assembly
assembly {
z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
}
}
/// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
z = (1 + cbrt(x)) * 10 ** 12;
z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
}
/// @solidity memory-safe-assembly
assembly {
let p := x
for {} 1 {} {
if iszero(shr(229, p)) {
if iszero(shr(199, p)) {
p := mul(p, 100000000000000000) // 10 ** 17.
break
}
p := mul(p, 100000000) // 10 ** 8.
break
}
if iszero(shr(249, p)) { p := mul(p, 100) }
break
}
let t := mulmod(mul(z, z), z, p)
z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := 1
if iszero(lt(x, 58)) {
mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
revert(0x1c, 0x04)
}
for {} x { x := sub(x, 1) } { z := mul(z, x) }
}
}
/// @dev Returns the log2 of `x`.
/// Equivalent to computing the index of the most significant bit (MSB) of `x`.
/// Returns 0 if `x` is zero.
function log2(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Returns the log2 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log2Up(uint256 x) internal pure returns (uint256 r) {
r = log2(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(r, 1), x))
}
}
/// @dev Returns the log10 of `x`.
/// Returns 0 if `x` is zero.
function log10(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(x, 100000000000000000000000000000000000000)) {
x := div(x, 100000000000000000000000000000000000000)
r := 38
}
if iszero(lt(x, 100000000000000000000)) {
x := div(x, 100000000000000000000)
r := add(r, 20)
}
if iszero(lt(x, 10000000000)) {
x := div(x, 10000000000)
r := add(r, 10)
}
if iszero(lt(x, 100000)) {
x := div(x, 100000)
r := add(r, 5)
}
r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
}
}
/// @dev Returns the log10 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log10Up(uint256 x) internal pure returns (uint256 r) {
r = log10(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(exp(10, r), x))
}
}
/// @dev Returns the log256 of `x`.
/// Returns 0 if `x` is zero.
function log256(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(shr(3, r), lt(0xff, shr(r, x)))
}
}
/// @dev Returns the log256 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log256Up(uint256 x) internal pure returns (uint256 r) {
r = log256(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(shl(3, r), 1), x))
}
}
/// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
/// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
/// @solidity memory-safe-assembly
assembly {
mantissa := x
if mantissa {
if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
mantissa := div(mantissa, 1000000000000000000000000000000000)
exponent := 33
}
if iszero(mod(mantissa, 10000000000000000000)) {
mantissa := div(mantissa, 10000000000000000000)
exponent := add(exponent, 19)
}
if iszero(mod(mantissa, 1000000000000)) {
mantissa := div(mantissa, 1000000000000)
exponent := add(exponent, 12)
}
if iszero(mod(mantissa, 1000000)) {
mantissa := div(mantissa, 1000000)
exponent := add(exponent, 6)
}
if iszero(mod(mantissa, 10000)) {
mantissa := div(mantissa, 10000)
exponent := add(exponent, 4)
}
if iszero(mod(mantissa, 100)) {
mantissa := div(mantissa, 100)
exponent := add(exponent, 2)
}
if iszero(mod(mantissa, 10)) {
mantissa := div(mantissa, 10)
exponent := add(exponent, 1)
}
}
}
}
/// @dev Convenience function for packing `x` into a smaller number using `sci`.
/// The `mantissa` will be in bits [7..255] (the upper 249 bits).
/// The `exponent` will be in bits [0..6] (the lower 7 bits).
/// Use `SafeCastLib` to safely ensure that the `packed` number is small
/// enough to fit in the desired unsigned integer type:
/// ```
/// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
/// ```
function packSci(uint256 x) internal pure returns (uint256 packed) {
(x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
/// @solidity memory-safe-assembly
assembly {
if shr(249, x) {
mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
revert(0x1c, 0x04)
}
packed := or(shl(7, x), packed)
}
}
/// @dev Convenience function for unpacking a packed number from `packSci`.
function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
unchecked {
unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards zero.
function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = (x & y) + ((x ^ y) >> 1);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
function avg(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = (x >> 1) + (y >> 1) + (x & y & 1);
}
}
/// @dev Returns the absolute value of `x`.
function abs(int256 x) internal pure returns (uint256 z) {
unchecked {
z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(int256 x, int256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(uint256 x, uint256 minValue, uint256 maxValue)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
/// @soli...
// [truncated — 55254 bytes total]
ICliqueLock.sol 87 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
struct StreamConfig {
address recipient;
address revokeAuthority;
address token;
uint256 amount;
uint256 startTime;
uint256 cliffTime;
uint256 endTime;
uint256 startUnlockPercentage;
uint256 cliffUnlockPercentage;
uint256 pieceDuration;
address verifier;
uint256 id;
}
/**
* @dev Stream struct containing all parameters for a token stream
* @param recipient Address that will receive the streamed tokens
* @param revokeAuthority Address with permission to revoke the stream
* @param token Address of the ERC20 token being streamed
* @param amount Total amount of tokens in the stream
* @param claimedAmount Amount of tokens already claimed
* @param startTime Timestamp when the stream begins
* @param cliffTime Timestamp when the cliff period ends
* @param endTime Timestamp when the stream ends
* @param startUnlockPercentage Percentage of tokens unlocked at start (18 decimals)
* @param cliffUnlockPercentage Percentage of tokens unlocked at cliff (18 decimals)
* @param pieceDuration Duration of each vesting piece (0 for linear vesting)
*/
struct Stream {
address recipient;
address revokeAuthority;
address token;
uint256 amount;
uint256 claimedAmount;
uint256 startTime;
uint256 cliffTime;
uint256 endTime;
uint256 startUnlockPercentage;
uint256 cliffUnlockPercentage;
uint256 pieceDuration;
}
struct Badge {
address verifier;
uint256 id;
}
interface IVerifier {
function ownerOf(uint256 id) external view returns (address);
}
interface ICliqueLock {
// EXTERNAL FUNCTIONS - STREAM CREATION
/**
* @notice Creates a stream using standard ERC20 approve
* @dev Requires prior approval of token transfer
* @param config StreamConfig struct containing the stream parameters
* @return streamId Unique identifier of the created stream
*/
function createStream(StreamConfig calldata config) external returns (uint256 streamId);
// EXTERNAL FUNCTIONS - STREAM REVOCATION
/**
* @notice Revokes a stream, returning unclaimed tokens to the revokeAuthority
* @dev Can only be called by the designated revokeAuthority
* @param streamId ID of the stream to revoke
*/
function revokeStream(uint256 streamId) external;
/**
* @notice Returns the stream information for a given streamId
* @param streamId ID of the stream to query
* @return stream Stream struct containing the stream information
*/
function streams(uint256 streamId) external view returns (Stream memory stream);
/**
* @notice Claims available tokens from a stream
* @dev Transfers all currently claimable tokens to the recipient
* @param streamId ID of the stream to claim from
*/
function claim(uint256 streamId) external;
}
Read Contract
badges 0x98e36d8b → address, uint256
calculateClaimableAmount 0x95c78fea → uint256
nextStreamId 0x1e99d569 → uint256
streams 0x64d60d91 → address, address, address, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256
Write Contract 5 functions
These functions modify contract state and require a wallet transaction to execute.
claim 0x379607f5
uint256 streamId
createStream 0xd9ec2925
tuple config
returns: uint256
createStreamWithPermit 0xb5342ad5
tuple config
uint256 deadline
bytes32 r
bytes32 s
uint8 v
returns: uint256
removeRevokeAuthority 0x690d450a
uint256 streamId
revokeStream 0xd0597f5c
uint256 streamId
Recent Transactions
Transaction index is loading. Only unfinalized transactions are shown while the index starts up.