Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x295eD2f7537883fDE8f5bf55cb6696a4a4Dbc70e
Balance 0 ETH
Nonce 1
Code Size 4689 bytes
Indexed Transactions Index loading...
External Etherscan · Sourcify

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.