ETH Price: $3,319.38 (-8.74%)

Contract

0x652a2A2876B855d9c30d9265F882F0a486608976
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
147223732022-05-06 8:02:15959 days ago1651824135  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
VestingModule

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 5 : VestingModule.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.13;

import {Clone} from "clones-with-immutable-args/Clone.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {FullMath} from "./lib/FullMath.sol";

///
/// @title VestingModule
/// @author 0xSplits <[email protected]>
/// @notice A maximally-composable vesting contract allowing multiple isolated
/// streams of different tokens to reach a beneficiary over time. Streams share
/// a vesting period but may begin or have funds released independently.
/// @dev Funds pile up in the contract via `receive()` & simple ERC20 `transfer`
/// until a caller creates a new vesting stream. The funds then vest linearly
/// over {vestingPeriod} and may be withdrawn accordingly by anyone on behalf
/// of the {beneficiary}. There is no limit on the number of simultaneous
/// vesting streams which may be created, ongoing or withdrawn from in a single
/// tx.
/// This contract uses address(0) in some fns/events/mappings to refer to ETH.
///
contract VestingModule is Clone {
    /// -----------------------------------------------------------------------
    /// errors
    /// -----------------------------------------------------------------------

    error InvalidVestingStreamId(uint256 id);

    /// -----------------------------------------------------------------------
    /// libraries
    /// -----------------------------------------------------------------------

    using SafeTransferLib for address;
    using SafeTransferLib for ERC20;

    /// -----------------------------------------------------------------------
    /// events
    /// -----------------------------------------------------------------------

    /// @notice New vesting stream created
    /// @param id Id of vesting stream
    /// @param token Address of token to vest (0x0 for ETH)
    /// @param amount Amount to vest
    event CreateVestingStream(
        uint256 indexed id,
        address indexed token,
        uint256 amount
    );

    /// @notice Release from vesting stream
    /// @param id Id of vesting stream
    /// @param amount Amount released from stream
    event ReleaseFromVestingStream(uint256 indexed id, uint256 amount);

    /// @notice Emitted after each successful ETH transfer to proxy
    /// @param amount Amount of ETH received
    event ReceiveETH(uint256 amount);

    /// -----------------------------------------------------------------------
    /// structs
    /// -----------------------------------------------------------------------

    /// @notice holds vesting stream metadata
    struct VestingStream {
        address token;
        uint256 vestingStart;
        uint256 total;
        uint256 released;
    }

    /// -----------------------------------------------------------------------
    /// storage
    /// -----------------------------------------------------------------------

    /// Address to receive funds after vesting
    /// @dev equivalent to address public immutable beneficiary;
    function beneficiary() public pure returns (address) {
        return _getArgAddress(0);
    }

    /// Period of time for funds to vest (defaults to 365 days)
    /// @dev equivalent to uint256 public immutable vestingPeriod;
    function vestingPeriod() public pure returns (uint256) {
        return _getArgUint256(20);
    }

    /// Number of vesting streams
    /// @dev Used for sequential ids
    uint256 public numVestingStreams;

    /// Mapping from Id to vesting stream
    mapping(uint256 => VestingStream) internal vestingStreams;
    /// Mapping from token to amount vesting (includes current & previous)
    mapping(address => uint256) public vesting;
    /// Mapping from token to amount released
    mapping(address => uint256) public released;

    /// -----------------------------------------------------------------------
    /// constructor
    /// -----------------------------------------------------------------------

    // solhint-disable-next-line no-empty-blocks
    constructor() {}

    /// -----------------------------------------------------------------------
    /// functions
    /// -----------------------------------------------------------------------

    /// -----------------------------------------------------------------------
    /// functions - public & external
    /// -----------------------------------------------------------------------

    /// @notice receive ETH
    /// @dev receive with emitted event is implemented w/i clone bytecode
    /* receive() external payable { */
    /*     emit ReceiveETH(msg.value); */
    /* } */

    /// @notice Creates new vesting streams
    /// @param tokens Addresses of ETH (0x0) & ERC20s to begin vesting
    /// @return ids Ids of created vesting streams for {tokens}
    function createVestingStreams(address[] calldata tokens)
        external
        payable
        returns (uint256[] memory ids)
    {
        uint256 numTokens = tokens.length;
        ids = new uint256[](numTokens);
        // use count as first new sequential id
        uint256 vestingStreamId = numVestingStreams;

        unchecked {
            // overflow should be impossible in for-loop index
            for (uint256 i = 0; i < numTokens; ++i) {
                address token = tokens[i];
                // overflow should be impossible
                // shouldn't need to worry about re-entrancy from ERC20 view fn
                // recognizes 0x0 as ETH
                // user chooses tokens array, pernicious ERC20 can't cause DoS
                // slither-disable-next-line calls-loop
                uint256 pendingAmount = (
                    token != address(0)
                        ? ERC20(token).balanceOf(address(this))
                        : address(this).balance
                    // vesting >= released
                ) - (vesting[token] - released[token]);
                vesting[token] += pendingAmount;
                // overflow should be impossible
                vestingStreams[vestingStreamId] = VestingStream({
                    token: token,
                    vestingStart: block.timestamp, // solhint-disable-line not-rely-on-time
                    total: pendingAmount,
                    released: 0
                });
                emit CreateVestingStream(vestingStreamId, token, pendingAmount);
                ids[i] = vestingStreamId;
                ++vestingStreamId;
            }
            // use last created id as new count
            numVestingStreams = vestingStreamId;
        }
    }

    /// @notice Releases vested funds to the beneficiary
    /// @param ids Ids of vesting streams to release funds from
    /// @return releasedFunds Amounts of funds released from vesting streams {ids}
    function releaseFromVesting(uint256[] calldata ids)
        external
        payable
        returns (uint256[] memory releasedFunds)
    {
        uint256 numIds = ids.length;
        releasedFunds = new uint256[](numIds);

        unchecked {
            // overflow should be impossible in for-loop index
            for (uint256 i = 0; i < numIds; ++i) {
                uint256 id = ids[i];
                if (id >= numVestingStreams) revert InvalidVestingStreamId(id);
                VestingStream memory vs = vestingStreams[id];
                uint256 transferAmount = _vestedAndUnreleased(vs);
                address token = vs.token;
                // overflow should be impossible
                vestingStreams[id].released += transferAmount;
                // overflow should be impossible
                released[token] += transferAmount;
                // don't need to worry about re-entrancy; funds can't be stolen from beneficiary
                // pernicious ERC20s would only mess their own storage, not brick the balance of any ERC20 or ETH
                if (token != address(0)) {
                    ERC20(token).safeTransfer(beneficiary(), transferAmount);
                } else {
                    beneficiary().safeTransferETH(transferAmount);
                }

                emit ReleaseFromVestingStream(id, transferAmount);
                releasedFunds[i] = transferAmount;
            }
        }
    }

    /// -----------------------------------------------------------------------
    /// functions - views
    /// -----------------------------------------------------------------------

    /// @notice View vesting stream {id}
    /// @param id Id of vesting stream to view
    /// @return vs Vesting stream
    function vestingStream(uint256 id)
        external
        view
        returns (VestingStream memory vs)
    {
        vs = vestingStreams[id];
    }

    /// @notice View vested amount in vesting stream {id}
    /// @param id Id of vesting stream to get vested amount of
    /// @return Amount vested in vesting stream {id}
    function vested(uint256 id) external view returns (uint256) {
        VestingStream memory vs = vestingStreams[id];
        return _vested(vs);
    }

    /// @notice View vested-and-unreleased amount in vesting stream {id}
    /// @param id Id of vesting stream to get vested-and-unreleased amount of
    /// @return Amount vested-and-unreleased in vesting stream {id}
    function vestedAndUnreleased(uint256 id) external view returns (uint256) {
        VestingStream memory vs = vestingStreams[id];
        return _vestedAndUnreleased(vs);
    }

    /// -----------------------------------------------------------------------
    /// functions - private & internal
    /// -----------------------------------------------------------------------

    /// @notice View vested amount in vesting stream {vs}
    /// @param vs Vesting stream to get vested amount of
    /// @return Amount vested in vesting stream {vs}
    function _vested(VestingStream memory vs) internal view returns (uint256) {
        uint256 elapsedTime;
        unchecked {
            // block.timestamp >= vs.vestingStart for any existing stream
            // solhint-disable-next-line not-rely-on-time
            elapsedTime = block.timestamp - vs.vestingStart;
        }
        return
            elapsedTime >= vestingPeriod()
                ? vs.total
                : FullMath.mulDiv(vs.total, elapsedTime, vestingPeriod());
    }

    /// @notice View vested-and-unreleased amount in vesting stream {vs}
    /// @param vs Vesting stream to get vested-and-unreleased amount of
    /// @return Amount vested-and-unreleased in vesting stream {vs}
    function _vestedAndUnreleased(VestingStream memory vs)
        internal
        view
        returns (uint256)
    {
        unchecked {
            // underflow should be impossible
            return _vested(vs) - vs.released;
        }
    }
}

File 2 of 5 : Clone.sol
// SPDX-License-Identifier: BSD
pragma solidity ^0.8.4;

/// @title Clone
/// @author zefram.eth
/// @notice Provides helper functions for reading immutable args from calldata
contract Clone {
    /// @notice Reads an immutable arg with type address
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgAddress(uint256 argOffset)
        internal
        pure
        returns (address arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0x60, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint256
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint256(uint256 argOffset)
        internal
        pure
        returns (uint256 arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := calldataload(add(offset, argOffset))
        }
    }

    /// @notice Reads a uint256 array stored in the immutable args.
    /// @param argOffset The offset of the arg in the packed data
    /// @param arrLen Number of elements in the array
    /// @return arr The array
    function _getArgUint256Array(uint256 argOffset, uint64 arrLen)
        internal
        pure
        returns (uint256[] memory arr)
    {
        uint256 offset = _getImmutableArgsOffset();
        uint256 el;
        arr = new uint256[](arrLen);
        for (uint64 i = 0; i < arrLen; i++) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                el := calldataload(add(add(offset, argOffset), mul(i, 32)))
            }
            arr[i] = el;
        }
        return arr;
    }

    /// @notice Reads an immutable arg with type uint64
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint64(uint256 argOffset)
        internal
        pure
        returns (uint64 arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xc0, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint8
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xf8, calldataload(add(offset, argOffset)))
        }
    }

    /// @return offset The offset of the packed immutable args in calldata
    function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            offset := sub(
                calldatasize(),
                add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
            )
        }
    }
}

File 3 of 5 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 4 of 5 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    event Debug(bool one, bool two, uint256 retsize);

    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 5 of 5 : FullMath.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
/// from https://github.com/ZeframLou/vested-erc20/blob/main/src/lib/FullMath.sol
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = a * b
            // 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 = prod1 * 2**256 + prod0
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(a, b, not(0))
                prod0 := mul(a, b)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division
            if (prod1 == 0) {
                require(denominator > 0);
                assembly {
                    result := div(prod0, denominator)
                }
                return result;
            }

            // Make sure the result is less than 2**256.
            // Also prevents denominator == 0
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0]
            // Compute remainder using mulmod
            uint256 remainder;
            assembly {
                remainder := mulmod(a, b, denominator)
            }
            // Subtract 256 bit number from 512 bit number
            assembly {
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator
            // Compute largest power of two divisor of denominator.
            // Always >= 1.
            uint256 twos = (type(uint256).max - denominator + 1) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            prod0 |= prod1 * twos;

            // Invert denominator mod 2**256
            // Now that denominator is an odd number, it has an inverse
            // modulo 2**256 such that denominator * inv = 1 mod 2**256.
            // Compute the inverse by starting with a seed that is correct
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // 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 *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // inverse mod 2**256

            // Because the division is now exact we can divide by multiplying
            // with the modular inverse of denominator. This will give us the
            // correct result modulo 2**256. Since the precoditions guarantee
            // that the outcome is less than 2**256, this is the final result.
            // We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inv;
            return result;
        }
    }

    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function mulDivRoundingUp(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        result = mulDiv(a, b, denominator);
        unchecked {
            if (mulmod(a, b, denominator) > 0) {
                require(result < type(uint256).max);
                result++;
            }
        }
    }
}

Settings
{
  "remappings": [
    "clones-with-immutable-args/=lib/clones-with-immutable-args/src/",
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "splits-contracts/=lib/splits-contracts/contracts/",
    "src/=src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london"
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"InvalidVestingStreamId","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreateVestingStream","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiveETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReleaseFromVestingStream","type":"event"},{"inputs":[],"name":"beneficiary","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"createVestingStreams","outputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"numVestingStreams","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"releaseFromVesting","outputs":[{"internalType":"uint256[]","name":"releasedFunds","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"released","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"vested","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"vestedAndUnreleased","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vesting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"vestingStream","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"vestingStart","type":"uint256"},{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"released","type":"uint256"}],"internalType":"struct VestingModule.VestingStream","name":"vs","type":"tuple"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50610af5806100206000396000f3fe6080604052600436106100915760003560e01c8063968fb8ba11610059578063968fb8ba1461018b5780639852595c146101ab578063a8b93e96146101d8578063cba7d6a9146101ee578063e388c4231461020157600080fd5b80632d18680e1461009657806338af3eed146100fb57806363efa49c146101285780637313ee5a146101565780639522873a1461016b575b600080fd5b3480156100a257600080fd5b506100b66100b1366004610966565b61022e565b6040516100f2919081516001600160a01b0316815260208083015190820152604080830151908201526060918201519181019190915260800190565b60405180910390f35b34801561010757600080fd5b506101106102af565b6040516001600160a01b0390911681526020016100f2565b34801561013457600080fd5b50610148610143366004610966565b6102c0565b6040519081526020016100f2565b34801561016257600080fd5b50610148610313565b61017e6101793660046109cb565b61031f565b6040516100f29190610a0d565b34801561019757600080fd5b506101486101a6366004610966565b61052c565b3480156101b757600080fd5b506101486101c6366004610a51565b60036020526000908152604090205481565b3480156101e457600080fd5b5061014860005481565b61017e6101fc3660046109cb565b610578565b34801561020d57600080fd5b5061014861021c366004610a51565b60026020526000908152604090205481565b610262604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b50600090815260016020818152604092839020835160808101855281546001600160a01b031681529281015491830191909152600281015492820192909252600390910154606082015290565b60006102bb600061074a565b905090565b6000818152600160208181526040808420815160808101835281546001600160a01b03168152938101549284019290925260028201549083015260030154606082015261030c8161076f565b9392505050565b60006102bb6014610786565b6060818067ffffffffffffffff81111561033b5761033b610a7a565b604051908082528060200260200182016040528015610364578160200160208202803683370190505b50600080549193505b8281101561052157600086868381811061038957610389610a90565b905060200201602081019061039e9190610a51565b6001600160a01b0381166000818152600360209081526040808320546002909252822054939450909203906103d3574761043b565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015610417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043b9190610aa6565b6001600160a01b038481166000818152600260208181526040808420805498909703978801909655855160808101875284815242818301908152818801898152606083018681528e87526001808652968a9020935184546001600160a01b031916991698909817835590519482019490945592519183019190915592516003909101559151838152929350909186917f9e99aba6e66a741d0793c014d6c18190a51f3559baaa8f5ce5c2cf3f685ded24910160405180910390a38386848151811061050857610508610a90565b602090810291909101015250506001918201910161036d565b506000555092915050565b6000818152600160208181526040808420815160808101835281546001600160a01b03168152938101549284019290925260028201549083015260030154606082015261030c816107a8565b6060818067ffffffffffffffff81111561059457610594610a7a565b6040519080825280602002602001820160405280156105bd578160200160208202803683370190505b50915060005b818110156107425760008585838181106105df576105df610a90565b90506020020135905060005481106106125760405163a94be08960e01b8152600481018290526024015b60405180910390fd5b6000818152600160208181526040808420815160808101835281546001600160a01b0316815293810154928401929092526002820154908301526003015460608201529061065f8261076f565b8251600085815260016020908152604080832060039081018054870190556001600160a01b038516808552925290912080548401905591925090156106bf576106ba6106a96102af565b6001600160a01b03831690846107e5565b6106da565b6106da826106cb6102af565b6001600160a01b031690610863565b837fac35c653955afa008e3d71e6d91e6feeff42bcc2eb62b48e4472beafe89ad4268360405161070c91815260200190565b60405180910390a28187868151811061072757610727610a90565b602002602001018181525050505050508060010190506105c3565b505092915050565b600080610761600119368181013560f01c90030190565b929092013560601c92915050565b6000816060015161077f836107a8565b0392915050565b60008061079d600119368181013560f01c90030190565b929092013592915050565b602081015160009042036107ba610313565b8110156107dc576107d78360400151826107d2610313565b6108b9565b61030c565b50506040015190565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061085d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610609565b50505050565b600080600080600085875af19050806108b45760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610609565b505050565b60008080600019858709858702925082811083820303915050806000036108f257600084116108e757600080fd5b50829004905061030c565b8084116108fe57600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b60006020828403121561097857600080fd5b5035919050565b60008083601f84011261099157600080fd5b50813567ffffffffffffffff8111156109a957600080fd5b6020830191508360208260051b85010111156109c457600080fd5b9250929050565b600080602083850312156109de57600080fd5b823567ffffffffffffffff8111156109f557600080fd5b610a018582860161097f565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b81811015610a4557835183529284019291840191600101610a29565b50909695505050505050565b600060208284031215610a6357600080fd5b81356001600160a01b038116811461030c57600080fd5b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600060208284031215610ab857600080fd5b505191905056fea2646970667358221220b92ac1519118e1a2a9e7f1f1e8e58e395a04eda59f458e5ad78160c405fd333a64736f6c634300080d0033

Deployed Bytecode

0x6080604052600436106100915760003560e01c8063968fb8ba11610059578063968fb8ba1461018b5780639852595c146101ab578063a8b93e96146101d8578063cba7d6a9146101ee578063e388c4231461020157600080fd5b80632d18680e1461009657806338af3eed146100fb57806363efa49c146101285780637313ee5a146101565780639522873a1461016b575b600080fd5b3480156100a257600080fd5b506100b66100b1366004610966565b61022e565b6040516100f2919081516001600160a01b0316815260208083015190820152604080830151908201526060918201519181019190915260800190565b60405180910390f35b34801561010757600080fd5b506101106102af565b6040516001600160a01b0390911681526020016100f2565b34801561013457600080fd5b50610148610143366004610966565b6102c0565b6040519081526020016100f2565b34801561016257600080fd5b50610148610313565b61017e6101793660046109cb565b61031f565b6040516100f29190610a0d565b34801561019757600080fd5b506101486101a6366004610966565b61052c565b3480156101b757600080fd5b506101486101c6366004610a51565b60036020526000908152604090205481565b3480156101e457600080fd5b5061014860005481565b61017e6101fc3660046109cb565b610578565b34801561020d57600080fd5b5061014861021c366004610a51565b60026020526000908152604090205481565b610262604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b50600090815260016020818152604092839020835160808101855281546001600160a01b031681529281015491830191909152600281015492820192909252600390910154606082015290565b60006102bb600061074a565b905090565b6000818152600160208181526040808420815160808101835281546001600160a01b03168152938101549284019290925260028201549083015260030154606082015261030c8161076f565b9392505050565b60006102bb6014610786565b6060818067ffffffffffffffff81111561033b5761033b610a7a565b604051908082528060200260200182016040528015610364578160200160208202803683370190505b50600080549193505b8281101561052157600086868381811061038957610389610a90565b905060200201602081019061039e9190610a51565b6001600160a01b0381166000818152600360209081526040808320546002909252822054939450909203906103d3574761043b565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015610417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043b9190610aa6565b6001600160a01b038481166000818152600260208181526040808420805498909703978801909655855160808101875284815242818301908152818801898152606083018681528e87526001808652968a9020935184546001600160a01b031916991698909817835590519482019490945592519183019190915592516003909101559151838152929350909186917f9e99aba6e66a741d0793c014d6c18190a51f3559baaa8f5ce5c2cf3f685ded24910160405180910390a38386848151811061050857610508610a90565b602090810291909101015250506001918201910161036d565b506000555092915050565b6000818152600160208181526040808420815160808101835281546001600160a01b03168152938101549284019290925260028201549083015260030154606082015261030c816107a8565b6060818067ffffffffffffffff81111561059457610594610a7a565b6040519080825280602002602001820160405280156105bd578160200160208202803683370190505b50915060005b818110156107425760008585838181106105df576105df610a90565b90506020020135905060005481106106125760405163a94be08960e01b8152600481018290526024015b60405180910390fd5b6000818152600160208181526040808420815160808101835281546001600160a01b0316815293810154928401929092526002820154908301526003015460608201529061065f8261076f565b8251600085815260016020908152604080832060039081018054870190556001600160a01b038516808552925290912080548401905591925090156106bf576106ba6106a96102af565b6001600160a01b03831690846107e5565b6106da565b6106da826106cb6102af565b6001600160a01b031690610863565b837fac35c653955afa008e3d71e6d91e6feeff42bcc2eb62b48e4472beafe89ad4268360405161070c91815260200190565b60405180910390a28187868151811061072757610727610a90565b602002602001018181525050505050508060010190506105c3565b505092915050565b600080610761600119368181013560f01c90030190565b929092013560601c92915050565b6000816060015161077f836107a8565b0392915050565b60008061079d600119368181013560f01c90030190565b929092013592915050565b602081015160009042036107ba610313565b8110156107dc576107d78360400151826107d2610313565b6108b9565b61030c565b50506040015190565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061085d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610609565b50505050565b600080600080600085875af19050806108b45760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610609565b505050565b60008080600019858709858702925082811083820303915050806000036108f257600084116108e757600080fd5b50829004905061030c565b8084116108fe57600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b60006020828403121561097857600080fd5b5035919050565b60008083601f84011261099157600080fd5b50813567ffffffffffffffff8111156109a957600080fd5b6020830191508360208260051b85010111156109c457600080fd5b9250929050565b600080602083850312156109de57600080fd5b823567ffffffffffffffff8111156109f557600080fd5b610a018582860161097f565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b81811015610a4557835183529284019291840191600101610a29565b50909695505050505050565b600060208284031215610a6357600080fd5b81356001600160a01b038116811461030c57600080fd5b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600060208284031215610ab857600080fd5b505191905056fea2646970667358221220b92ac1519118e1a2a9e7f1f1e8e58e395a04eda59f458e5ad78160c405fd333a64736f6c634300080d0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.