ETH Price: $3,065.81 (+0.93%)
Gas: 4 Gwei

Contract

0xa64Fe2d9371aA9De89578A01CECa9835FDd60dc7
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Withdraw200005012024-06-02 0:17:1137 days ago1717287431IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.000246054.96958815
Withdraw198527752024-05-12 8:33:3558 days ago1715502815IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.000187713.79118278
Withdraw196455482024-04-13 8:45:1187 days ago1712997911IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0006115912.3521599
Withdraw Payer196250992024-04-10 12:02:2390 days ago1712750543IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0013275420.48491507
Withdraw195667682024-04-02 7:55:4798 days ago1712044547IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.002960159.78430724
Withdraw195500162024-03-30 23:26:35100 days ago1711841195IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.001114522.50934308
Withdraw195497502024-03-30 22:32:11100 days ago1711837931IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.001096622.14774735
Withdraw195490082024-03-30 20:02:11100 days ago1711828931IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0012177424.59450621
Withdraw195479862024-03-30 16:35:23100 days ago1711816523IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0012344524.93198857
Withdraw195479682024-03-30 16:31:47100 days ago1711816307IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0015965532.24524627
Withdraw195146192024-03-25 23:02:35105 days ago1711407755IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0014112828.50330116
Withdraw195138702024-03-25 20:31:11105 days ago1711398671IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0021629543.68457065
Batch195138702024-03-25 20:31:11105 days ago1711398671IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.00225643.68457065
Withdraw195073642024-03-24 22:29:11106 days ago1711319351IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0009131118.44201644
Withdraw195072842024-03-24 22:13:11106 days ago1711318391IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0009226518.63455693
Withdraw194990932024-03-23 18:29:59107 days ago1711218599IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0009940320.07617286
Batch194924052024-03-22 20:00:23108 days ago1711137623IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0299870519.592849
Withdraw194923922024-03-22 19:57:47108 days ago1711137467IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0012631621.51423714
Withdraw Payer A...194914942024-03-22 16:56:11108 days ago1711126571IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0019137227.43728814
Withdraw194850092024-03-21 19:07:47109 days ago1711048067IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0017133827.10879034
Deposit194846062024-03-21 17:46:47109 days ago1711043207IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0022378430.63195068
Withdraw194841632024-03-21 16:16:35109 days ago1711037795IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0029268136.44664615
Withdraw194841572024-03-21 16:15:23109 days ago1711037723IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0029285736.46859763
Withdraw194841482024-03-21 16:13:35109 days ago1711037615IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0026062632.45503601
Withdraw194841452024-03-21 16:12:47109 days ago1711037567IN
0xa64Fe2d9...5FDd60dc7
0 ETH0.0028439335.41461306
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
193046902024-02-25 12:47:11135 days ago1708865231  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x60c7B0c5...7aA287Ff2
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
LlamaPay

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 999999 runs

Other Settings:
default evmVersion
File 1 of 5 : LlamaPay.sol
//SPDX-License-Identifier: None
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BoringBatchable} from "./fork/BoringBatchable.sol";

interface Factory {
    function parameter() external view returns (address);
}

interface IERC20WithDecimals {
    function decimals() external view returns (uint8);
}

// All amountPerSec and all internal numbers use 20 decimals, these are converted to the right decimal on withdrawal/deposit
// The reason for that is to minimize precision errors caused by integer math on tokens with low decimals (eg: USDC)

// Invariant through the whole contract: lastPayerUpdate[anyone] <= block.timestamp
// Reason: timestamps can't go back in time (https://github.com/ethereum/go-ethereum/blob/master/consensus/ethash/consensus.go#L274 and block timestamp definition on ethereum's yellow paper)
// and we always set lastPayerUpdate[anyone] either to the current block.timestamp or a value lower than it
// We could use this to optimize subtractions and avoid an unneded safemath check there for some gas savings
// However this is obscure enough that we are not sure if a future ethereum network upgrade might remove this assertion
// or if an ethereum fork might remove that code and invalidate the condition, causing our deployment on that chain to be vulnerable
// This is dangerous because if someone can make a timestamp go back into the past they could steal all the money
// So we forgo these optimizations and instead enforce this condition.

// Another assumption is that all timestamps can fit in uint40, this will be true until year 231,800, so it's a safe assumption

contract LlamaPay is BoringBatchable {
    using SafeERC20 for IERC20;

    struct Payer {
        uint40 lastPayerUpdate;
        uint216 totalPaidPerSec; // uint216 is enough to hold 1M streams of 3e51 tokens/yr, which is enough
    }

    mapping (bytes32 => uint) public streamToStart;
    mapping (address => Payer) public payers;
    mapping (address => uint) public balances; // could be packed together with lastPayerUpdate but gains are not high
    IERC20 public token;
    uint public DECIMALS_DIVISOR;

    event StreamCreated(address indexed from, address indexed to, uint216 amountPerSec, bytes32 streamId);
    event StreamCreatedWithReason(address indexed from, address indexed to, uint216 amountPerSec, bytes32 streamId, string reason);
    event StreamCancelled(address indexed from, address indexed to, uint216 amountPerSec, bytes32 streamId);
    event StreamPaused(address indexed from, address indexed to, uint216 amountPerSec, bytes32 streamId);
    event StreamModified(address indexed from, address indexed oldTo, uint216 oldAmountPerSec, bytes32 oldStreamId, address indexed to, uint216 amountPerSec, bytes32 newStreamId);
    event Withdraw(address indexed from, address indexed to, uint216 amountPerSec, bytes32 streamId, uint amount);
    event PayerDeposit(address indexed from, uint amount);
    event PayerWithdraw(address indexed from, uint amount);

    constructor(){
        token = IERC20(Factory(msg.sender).parameter());
        uint8 tokenDecimals = IERC20WithDecimals(address(token)).decimals();
        DECIMALS_DIVISOR = 10**(20 - tokenDecimals);
    }

    function getStreamId(address from, address to, uint216 amountPerSec) public pure returns (bytes32){
        return keccak256(abi.encodePacked(from, to, amountPerSec));
    }

    function _createStream(address to, uint216 amountPerSec) internal returns (bytes32 streamId){
        streamId = getStreamId(msg.sender, to, amountPerSec);
        require(amountPerSec > 0, "amountPerSec can't be 0");
        require(streamToStart[streamId] == 0, "stream already exists");
        streamToStart[streamId] = block.timestamp;

        Payer storage payer = payers[msg.sender];
        uint totalPaid;
        uint delta = block.timestamp - payer.lastPayerUpdate;
        unchecked {
            totalPaid = delta * uint(payer.totalPaidPerSec);
        }
        balances[msg.sender] -= totalPaid; // implicit check that balance >= totalPaid, can't create a new stream unless there's no debt

        payer.lastPayerUpdate = uint40(block.timestamp);
        payer.totalPaidPerSec += amountPerSec;

        // checking that no overflow will ever happen on totalPaidPerSec is important because if there's an overflow later:
        //   - if we don't have overflow checks -> it would be possible to steal money from other people
        //   - if there are overflow checks -> money will be stuck forever as all txs (from payees of the same payer) will revert
        //     which can be used to rug employees and make them unable to withdraw their earnings
        // Thus it's extremely important that no user is allowed to enter any value that later on could trigger an overflow.
        // We implicitly prevent this here because amountPerSec/totalPaidPerSec is uint216 and is only ever multiplied by timestamps
        // which will always fit in a uint40. Thus the result of the multiplication will always fit inside a uint256 and never overflow
        // This however introduces a new invariant: the only operations that can be done with amountPerSec/totalPaidPerSec are muls against timestamps
        // and we need to make sure they happen in uint256 contexts, not any other
    }

    function createStream(address to, uint216 amountPerSec) public {
        bytes32 streamId = _createStream(to, amountPerSec);
        emit StreamCreated(msg.sender, to, amountPerSec, streamId);
    }

    function createStreamWithReason(address to, uint216 amountPerSec, string calldata reason) public {
        bytes32 streamId = _createStream(to, amountPerSec);
        emit StreamCreatedWithReason(msg.sender, to, amountPerSec, streamId, reason);
    }

    /*
        proof that lastUpdate < block.timestamp:

        let's start by assuming the opposite, that lastUpdate > block.timestamp, and then we'll prove that this is impossible
        lastUpdate > block.timestamp
            -> timePaid = lastUpdate - lastPayerUpdate[from] > block.timestamp - lastPayerUpdate[from] = payerDelta
            -> timePaid > payerDelta
            -> payerBalance = timePaid * totalPaidPerSec[from] > payerDelta * totalPaidPerSec[from] = totalPayerPayment
            -> payerBalance > totalPayerPayment
        but this last statement is impossible because if it were true we'd have gone into the first if branch!
    */
    /*
        proof that totalPaidPerSec[from] != 0:

        totalPaidPerSec[from] is a sum of uint that are different from zero (since we test that on createStream())
        and we test that there's at least one stream active with `streamToStart[streamId] != 0`,
        so it's a sum of one or more elements that are higher than zero, thus it can never be zero
    */

    // Make it possible to withdraw on behalf of others, important for people that don't have a metamask wallet (eg: cex address, trustwallet...)
    function _withdraw(address from, address to, uint216 amountPerSec) private returns (uint40 lastUpdate, bytes32 streamId, uint amountToTransfer) {
        streamId = getStreamId(from, to, amountPerSec);
        require(streamToStart[streamId] != 0, "stream doesn't exist");

        Payer storage payer = payers[from];
        uint totalPayerPayment;
        uint payerDelta = block.timestamp - payer.lastPayerUpdate;
        unchecked{
            totalPayerPayment = payerDelta * uint(payer.totalPaidPerSec);
        }
        uint payerBalance = balances[from];
        if(payerBalance >= totalPayerPayment){
            unchecked {
                balances[from] = payerBalance - totalPayerPayment;   
            }
            lastUpdate = uint40(block.timestamp);
        } else {
            // invariant: totalPaidPerSec[from] != 0
            unchecked {
                uint timePaid = payerBalance/uint(payer.totalPaidPerSec);
                lastUpdate = uint40(payer.lastPayerUpdate + timePaid);
                // invariant: lastUpdate < block.timestamp (we need to maintain it)
                balances[from] = payerBalance % uint(payer.totalPaidPerSec);
            }
        }
        uint delta = lastUpdate - streamToStart[streamId]; // Could use unchecked here too I think
        unchecked {
            // We push transfers to be done outside this function and at the end of public functions to avoid reentrancy exploits
            amountToTransfer = (delta*uint(amountPerSec))/DECIMALS_DIVISOR;
        }
        emit Withdraw(from, to, amountPerSec, streamId, amountToTransfer);
    }

    // Copy of _withdraw that is view-only and returns how much can be withdrawn from a stream, purely for convenience on frontend
    // No need to review since this does nothing
    function withdrawable(address from, address to, uint216 amountPerSec) external view returns (uint withdrawableAmount, uint lastUpdate, uint owed) {
        bytes32 streamId = getStreamId(from, to, amountPerSec);
        require(streamToStart[streamId] != 0, "stream doesn't exist");

        Payer storage payer = payers[from];
        uint totalPayerPayment;
        uint payerDelta = block.timestamp - payer.lastPayerUpdate;
        unchecked{
            totalPayerPayment = payerDelta * uint(payer.totalPaidPerSec);
        }
        uint payerBalance = balances[from];
        if(payerBalance >= totalPayerPayment){
            lastUpdate = block.timestamp;
        } else {
            unchecked {
                uint timePaid = payerBalance/uint(payer.totalPaidPerSec);
                lastUpdate = payer.lastPayerUpdate + timePaid;
            }
        }
        uint delta = lastUpdate - streamToStart[streamId];
        withdrawableAmount = (delta*uint(amountPerSec))/DECIMALS_DIVISOR;
        owed = ((block.timestamp - lastUpdate)*uint(amountPerSec))/DECIMALS_DIVISOR;
    }

    function withdraw(address from, address to, uint216 amountPerSec) external {
        (uint40 lastUpdate, bytes32 streamId, uint amountToTransfer) = _withdraw(from, to, amountPerSec);
        streamToStart[streamId] = lastUpdate;
        payers[from].lastPayerUpdate = lastUpdate;
        token.safeTransfer(to, amountToTransfer);
    }

    function _cancelStream(address to, uint216 amountPerSec) internal returns (bytes32 streamId) {
        uint40 lastUpdate; uint amountToTransfer;
        (lastUpdate, streamId, amountToTransfer) = _withdraw(msg.sender, to, amountPerSec);
        streamToStart[streamId] = 0;
        Payer storage payer = payers[msg.sender];
        unchecked{
            // totalPaidPerSec is a sum of items which include amountPerSec, so totalPaidPerSec >= amountPerSec
            payer.totalPaidPerSec -= amountPerSec;
        }
        payer.lastPayerUpdate = lastUpdate;
        token.safeTransfer(to, amountToTransfer);
    }

    function cancelStream(address to, uint216 amountPerSec) public {
        bytes32 streamId = _cancelStream(to, amountPerSec);
        emit StreamCancelled(msg.sender, to, amountPerSec, streamId);
    }

    function pauseStream(address to, uint216 amountPerSec) public {
        bytes32 streamId = _cancelStream(to, amountPerSec);
        emit StreamPaused(msg.sender, to, amountPerSec, streamId);
    }

    function modifyStream(address oldTo, uint216 oldAmountPerSec, address to, uint216 amountPerSec) external {
        // Can be optimized but I don't think extra complexity is worth it
        bytes32 oldStreamId = _cancelStream(oldTo, oldAmountPerSec);
        bytes32 newStreamId = _createStream(to, amountPerSec);
        emit StreamModified(msg.sender, oldTo, oldAmountPerSec, oldStreamId, to, amountPerSec, newStreamId);
    }

    function deposit(uint amount) public {
        balances[msg.sender] += amount * DECIMALS_DIVISOR;
        token.safeTransferFrom(msg.sender, address(this), amount);
        emit PayerDeposit(msg.sender, amount);
    }

    function depositAndCreate(uint amountToDeposit, address to, uint216 amountPerSec) external {
        deposit(amountToDeposit);
        createStream(to, amountPerSec);
    }

    function depositAndCreateWithReason(uint amountToDeposit, address to, uint216 amountPerSec, string calldata reason) external {
        deposit(amountToDeposit);
        createStreamWithReason(to, amountPerSec, reason);
    }

    function withdrawPayer(uint amount) public {
        Payer storage payer = payers[msg.sender];
        balances[msg.sender] -= amount; // implicit check that balance > amount
        uint delta = block.timestamp - payer.lastPayerUpdate;
        unchecked {
            require(balances[msg.sender] >= delta*uint(payer.totalPaidPerSec), "pls no rug");
            uint tokenAmount = amount/DECIMALS_DIVISOR;
            token.safeTransfer(msg.sender, tokenAmount);
            emit PayerWithdraw(msg.sender, tokenAmount);
        }
    }

    function withdrawPayerAll() external {
        Payer storage payer = payers[msg.sender];
        unchecked {
            uint delta = block.timestamp - payer.lastPayerUpdate;
            // Just helper function, nothing happens if number is wrong
            // If there's an overflow it's just equivalent to calling withdrawPayer() directly with a big number
            withdrawPayer(balances[msg.sender]-delta*uint(payer.totalPaidPerSec));
        }
    }

    function getPayerBalance(address payerAddress) external view returns (int) {
        Payer storage payer = payers[payerAddress];
        int balance = int(balances[payerAddress]);
        uint delta = block.timestamp - payer.lastPayerUpdate;
        return (balance - int(delta*uint(payer.totalPaidPerSec)))/int(DECIMALS_DIVISOR);
    }
}

File 2 of 5 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount
    ) external returns (bool);

    /**
     * @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);
}

File 3 of 5 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 4 of 5 : BoringBatchable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

// solhint-disable avoid-low-level-calls
// solhint-disable no-inline-assembly

// WARNING!!!
// Combining BoringBatchable with msg.value can cause double spending issues
// https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong/

interface IERC20Permit{
     /// @notice EIP 2612
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

contract BaseBoringBatchable {
    /// @dev Helper function to extract a useful revert message from a failed call.
    /// If the returned data is malformed or not correctly abi encoded then this call can fail itself.
    function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)
        if (_returnData.length < 68) return "Transaction reverted silently";

        assembly {
            // Slice the sighash.
            _returnData := add(_returnData, 0x04)
        }
        return abi.decode(_returnData, (string)); // All that remains is the revert string
    }

    /// @notice Allows batched call to self (this contract).
    /// @param calls An array of inputs for each call.
    /// @param revertOnFail If True then reverts after a failed call and stops doing further calls.
    // F1: External is ok here because this is the batch function, adding it to a batch makes no sense
    // F2: Calls in the batch may be payable, delegatecall operates in the same context, so each call in the batch has access to msg.value
    // C3: The length of the loop is fully under user control, so can't be exploited
    // C7: Delegatecall is only used on the same contract, so it's safe
    function batch(bytes[] calldata calls, bool revertOnFail) external payable {
        for (uint256 i = 0; i < calls.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(calls[i]);
            if (!success && revertOnFail) {
                revert(_getRevertMsg(result));
            }
        }
    }
}

contract BoringBatchable is BaseBoringBatchable {
    /// @notice Call wrapper that performs `ERC20.permit` on `token`.
    /// Lookup `IERC20.permit`.
    // F6: Parameters can be used front-run the permit and the user's permit will fail (due to nonce or other revert)
    //     if part of a batch this could be used to grief once as the second call would not need the permit
    function permitToken(
        IERC20Permit token,
        address from,
        address to,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public {
        token.permit(from, to, amount, deadline, v, r, s);
    }
}

File 5 of 5 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PayerDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PayerWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"indexed":false,"internalType":"bytes32","name":"streamId","type":"bytes32"}],"name":"StreamCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"indexed":false,"internalType":"bytes32","name":"streamId","type":"bytes32"}],"name":"StreamCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"indexed":false,"internalType":"bytes32","name":"streamId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"StreamCreatedWithReason","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"oldTo","type":"address"},{"indexed":false,"internalType":"uint216","name":"oldAmountPerSec","type":"uint216"},{"indexed":false,"internalType":"bytes32","name":"oldStreamId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"indexed":false,"internalType":"bytes32","name":"newStreamId","type":"bytes32"}],"name":"StreamModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"indexed":false,"internalType":"bytes32","name":"streamId","type":"bytes32"}],"name":"StreamPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"indexed":false,"internalType":"bytes32","name":"streamId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DECIMALS_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"calls","type":"bytes[]"},{"internalType":"bool","name":"revertOnFail","type":"bool"}],"name":"batch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"cancelStream","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"createStream","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"internalType":"string","name":"reason","type":"string"}],"name":"createStreamWithReason","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToDeposit","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"depositAndCreate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToDeposit","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"},{"internalType":"string","name":"reason","type":"string"}],"name":"depositAndCreateWithReason","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"payerAddress","type":"address"}],"name":"getPayerBalance","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"getStreamId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"oldTo","type":"address"},{"internalType":"uint216","name":"oldAmountPerSec","type":"uint216"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"modifyStream","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"pauseStream","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"payers","outputs":[{"internalType":"uint40","name":"lastPayerUpdate","type":"uint40"},{"internalType":"uint216","name":"totalPaidPerSec","type":"uint216"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Permit","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permitToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"streamToStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawPayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawPayerAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint216","name":"amountPerSec","type":"uint216"}],"name":"withdrawable","outputs":[{"internalType":"uint256","name":"withdrawableAmount","type":"uint256"},{"internalType":"uint256","name":"lastUpdate","type":"uint256"},{"internalType":"uint256","name":"owed","type":"uint256"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x60806040526004361061016a5760003560e01c80637c516e94116100cb578063b6b55f251161007f578063c6a6477111610059578063c6a6477114610459578063d2423b5114610479578063fc0c546a1461048c57600080fd5b8063b6b55f25146103f9578063bfda0b4514610419578063c355f3431461043957600080fd5b80638835510c116100b05780638835510c146103a4578063a05860e0146103c4578063a3f83f6e146103e457600080fd5b80637c516e9414610364578063807a379c1461038457600080fd5b80634a714c24116101225780635ed1b15d116101075780635ed1b15d146102f75780636bc16095146103175780636e85975e1461034457600080fd5b80634a714c24146102425780635b0c2f2f146102d757600080fd5b806327e235e31161015357806327e235e3146101b15780632b4146f8146101f15780633f053acd1461020757600080fd5b806317a566e41461016f5780632087652c14610191575b600080fd5b34801561017b57600080fd5b5061018f61018a366004611b99565b6104de565b005b34801561019d57600080fd5b5061018f6101ac366004611ee9565b610573565b3480156101bd57600080fd5b506101de6101cc366004611b7d565b60026020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156101fd57600080fd5b506101de60045481565b34801561021357600080fd5b50610227610222366004611b99565b61058f565b604080519384526020840192909252908201526060016101e8565b34801561024e57600080fd5b5061029e61025d366004611b7d565b60016020526000908152604090205464ffffffffff8116906501000000000090047affffffffffffffffffffffffffffffffffffffffffffffffffffff1682565b6040805164ffffffffff90931683527affffffffffffffffffffffffffffffffffffffffffffffffffffff9091166020830152016101e8565b3480156102e357600080fd5b5061018f6102f2366004611bdf565b6107d3565b34801561030357600080fd5b5061018f610312366004611ec3565b610858565b34801561032357600080fd5b506101de610332366004611d68565b60006020819052908152604090205481565b34801561035057600080fd5b506101de61035f366004611b7d565b610870565b34801561037057600080fd5b5061018f61037f366004611d80565b61090b565b34801561039057600080fd5b5061018f61039f366004611bdf565b6109c3565b3480156103b057600080fd5b5061018f6103bf366004611c6a565b610a3f565b3480156103d057600080fd5b506101de6103df366004611b99565b610abf565b3480156103f057600080fd5b5061018f610b4a565b34801561040557600080fd5b5061018f610414366004611d68565b610ba9565b34801561042557600080fd5b5061018f610434366004611d68565b610c37565b34801561044557600080fd5b5061018f610454366004611bdf565b610dbf565b34801561046557600080fd5b5061018f610474366004611c13565b610e3b565b61018f610487366004611ccb565b610ee4565b34801561049857600080fd5b506003546104b99073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e8565b60008060006104ee868686610ff8565b60008281526020818152604080832064ffffffffff87169081905573ffffffffffffffffffffffffffffffffffffffff808e1685526001909352922080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000016909217909155600354939650919450925061056b9116868361134e565b505050505050565b61057c85610ba9565b61058884848484610a3f565b5050505050565b6000806000806105a0878787610abf565b60008181526020819052604090205490915061061d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f73747265616d20646f65736e277420657869737400000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff871660009081526001602052604081208054909190819061065a9064ffffffffff1642612234565b835473ffffffffffffffffffffffffffffffffffffffff8c16600090815260026020526040902054650100000000009091047affffffffffffffffffffffffffffffffffffffffffffffffffffff16820293509091508281106106bf57429650610730565b83546000906501000000000090047affffffffffffffffffffffffffffffffffffffffffffffffffffff16828161071f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b865464ffffffffff16919004019750505b6000858152602081905260408120546107499089612234565b6004549091506107767affffffffffffffffffffffffffffffffffffffffffffffffffffff8c1683612183565b610780919061216f565b6004549099507affffffffffffffffffffffffffffffffffffffffffffffffffffff8b166107ae8a42612234565b6107b89190612183565b6107c2919061216f565b965050505050505093509350939050565b60006107df8383611422565b604080517affffffffffffffffffffffffffffffffffffffffffffffffffffff851681526020810183905291925073ffffffffffffffffffffffffffffffffffffffff85169133917fad944a97ab4c0b30123a6c2757ec85eab70d01af114e49d0f0a86c466e32fb4f91015b60405180910390a3505050565b61086183610ba9565b61086b8282610dbf565b505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001602090815260408083206002909252822054815483906108b59064ffffffffff1642612234565b6004548454919250906108ee906501000000000090047affffffffffffffffffffffffffffffffffffffffffffffffffffff1683612183565b6108f890846121c0565b6109029190612107565b95945050505050565b6040517fd505accf00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88811660048301528781166024830152604482018790526064820186905260ff8516608483015260a4820184905260c4820183905289169063d505accf9060e401600060405180830381600087803b1580156109a157600080fd5b505af11580156109b5573d6000803e3d6000fd5b505050505050505050505050565b60006109cf8383611422565b604080517affffffffffffffffffffffffffffffffffffffffffffffffffffff851681526020810183905291925073ffffffffffffffffffffffffffffffffffffffff85169133917f6841f88c613e9eedd999a7291168f6d1f83bdd2b2416f98e91c01c19d6d5763a910161084b565b6000610a4b85856114e2565b90508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167ff9b31dda66c763701e363e12ac3126c9030e68c82e939dbd42c33a8021e6dda886848787604051610ab09493929190611fd5565b60405180910390a35050505050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085811b8216602084015284901b1660348201527fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000602883901b1660488201526000906063016040516020818303038152906040528051906020012090505b9392505050565b3360009081526001602090815260408083208054600290935292205464ffffffffff8216420391610ba591650100000000009091047affffffffffffffffffffffffffffffffffffffffffffffffffffff1683029003610c37565b5050565b600454610bb69082612183565b3360009081526002602052604081208054909190610bd59084906120ef565b9091555050600354610bff9073ffffffffffffffffffffffffffffffffffffffff16333084611737565b60405181815233907f3de3aee7860cdac45b499418398cd4bcf1f9952148868b8a6d43ba7729a0bbb59060200160405180910390a250565b33600090815260016020908152604080832060029092528220805491928492610c61908490612234565b90915550508054600090610c7c9064ffffffffff1642612234565b8254336000908152600260205260409020549192506501000000000090047affffffffffffffffffffffffffffffffffffffffffffffffffffff1682021115610d21576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f706c73206e6f20727567000000000000000000000000000000000000000000006044820152606401610614565b60006004548481610d5b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6003549190049150610d849073ffffffffffffffffffffffffffffffffffffffff16338361134e565b60405181815233907fb67efc8b8dcfed4c807ecc6c0e5c1234071c787585f1fef08e7cb9a1bc7c7de99060200160405180910390a250505050565b6000610dcb83836114e2565b604080517affffffffffffffffffffffffffffffffffffffffffffffffffffff851681526020810183905291925073ffffffffffffffffffffffffffffffffffffffff85169133917fbdf2ad452fb5e7123ebc3e977452088bc6ac9aa4c6b807fa043bb65f7dd62810910161084b565b6000610e478585611422565b90506000610e5584846114e2565b604080517affffffffffffffffffffffffffffffffffffffffffffffffffffff88811682526020820186905286168183015260608101839052905191925073ffffffffffffffffffffffffffffffffffffffff868116929089169133917f0edc6e7edb775c1fb5a1326f1b041b17e4e1cbfb9ea4a0562920bda1a7d031349181900360800190a4505050505050565b60005b82811015610ff25760008030868685818110610f2c577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9050602002810190610f3e919061204d565b604051610f4c929190611f58565b600060405180830381855af49150503d8060008114610f87576040519150601f19603f3d011682016040523d82523d6000602084013e610f8c565b606091505b509150915081158015610f9c5750835b15610fdd57610faa81611795565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106149190611f84565b50508080610fea90612277565b915050610ee7565b50505050565b6000806000611008868686610abf565b600081815260208190526040902054909250611080576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f73747265616d20646f65736e27742065786973740000000000000000000000006044820152606401610614565b73ffffffffffffffffffffffffffffffffffffffff86166000908152600160205260408120805490919081906110bd9064ffffffffff1642612234565b835473ffffffffffffffffffffffffffffffffffffffff8b16600090815260026020526040902054650100000000009091047affffffffffffffffffffffffffffffffffffffffffffffffffffff168202935090915082811061114c5773ffffffffffffffffffffffffffffffffffffffff8a1660009081526002602052604090208382039055429650611246565b83546000906501000000000090047affffffffffffffffffffffffffffffffffffffffffffffffffffff1682816111ac577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b865491900464ffffffffff82168101995091506501000000000090047affffffffffffffffffffffffffffffffffffffffffffffffffffff16828161121a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff8d1660009081526002602052604090209190069055505b6000868152602081905260408120546112669064ffffffffff8a16612234565b9050600454897affffffffffffffffffffffffffffffffffffffffffffffffffffff168202816112bf577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b604080517affffffffffffffffffffffffffffffffffffffffffffffffffffff8d168152602081018b905292909104908201819052965073ffffffffffffffffffffffffffffffffffffffff808c1691908d16907f674faf747e3eea06e0c6346405d01330e029b752733b8ac5cfa9365ed64a496c9060600160405180910390a3505050505093509350939050565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261086b9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526117fa565b6000806000611432338686610ff8565b60008281526020818152604080832083905533835260019091529020805464ffffffffff85167affffffffffffffffffffffffffffffffffffffffffffffffffffff650100000000009283900481168b9003169091027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001617815560035492965092945092506114d99073ffffffffffffffffffffffffffffffffffffffff16878461134e565b50505092915050565b60006114ef338484610abf565b90506000827affffffffffffffffffffffffffffffffffffffffffffffffffffff1611611578576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f616d6f756e745065725365632063616e277420626520300000000000000000006044820152606401610614565b600081815260208190526040902054156115ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f73747265616d20616c72656164792065786973747300000000000000000000006044820152606401610614565b600081815260208181526040808320429081905533845260019092528220805490929182916116269164ffffffffff90911690612234565b83543360009081526002602052604081208054650100000000009093047affffffffffffffffffffffffffffffffffffffffffffffffffffff1684029550929350849291611675908490612234565b909155505082547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617808455859084906005906116e29084906501000000000090047affffffffffffffffffffffffffffffffffffffffffffffffffffff166120b0565b92506101000a8154817affffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837affffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555050505092915050565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610ff29085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016113a0565b60606044825110156117da57505060408051808201909152601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b600482019150818060200190518101906117f49190611e07565b92915050565b600061185c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166119069092919063ffffffff16565b80519091501561086b578080602001905181019061187a9190611d4c565b61086b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610614565b6060611915848460008561191d565b949350505050565b6060824710156119af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610614565b73ffffffffffffffffffffffffffffffffffffffff85163b611a2d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610614565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051611a569190611f68565b60006040518083038185875af1925050503d8060008114611a93576040519150601f19603f3d011682016040523d82523d6000602084013e611a98565b606091505b5091509150611aa8828286611ab3565b979650505050505050565b60608315611ac2575081610b43565b825115611ad25782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106149190611f84565b60008083601f840112611b17578182fd5b50813567ffffffffffffffff811115611b2e578182fd5b602083019150836020828501011115611b4657600080fd5b9250929050565b80357affffffffffffffffffffffffffffffffffffffffffffffffffffff81168114611b7857600080fd5b919050565b600060208284031215611b8e578081fd5b8135610b438161233d565b600080600060608486031215611bad578182fd5b8335611bb88161233d565b92506020840135611bc88161233d565b9150611bd660408501611b4d565b90509250925092565b60008060408385031215611bf1578182fd5b8235611bfc8161233d565b9150611c0a60208401611b4d565b90509250929050565b60008060008060808587031215611c28578081fd5b8435611c338161233d565b9350611c4160208601611b4d565b92506040850135611c518161233d565b9150611c5f60608601611b4d565b905092959194509250565b60008060008060608587031215611c7f578384fd5b8435611c8a8161233d565b9350611c9860208601611b4d565b9250604085013567ffffffffffffffff811115611cb3578283fd5b611cbf87828801611b06565b95989497509550505050565b600080600060408486031215611cdf578283fd5b833567ffffffffffffffff80821115611cf6578485fd5b818601915086601f830112611d09578485fd5b813581811115611d17578586fd5b8760208260051b8501011115611d2b578586fd5b60209283019550935050840135611d4181612362565b809150509250925092565b600060208284031215611d5d578081fd5b8151610b4381612362565b600060208284031215611d79578081fd5b5035919050565b600080600080600080600080610100898b031215611d9c578384fd5b8835611da78161233d565b97506020890135611db78161233d565b96506040890135611dc78161233d565b9550606089013594506080890135935060a089013560ff81168114611dea578384fd5b979a969950949793969295929450505060c08201359160e0013590565b600060208284031215611e18578081fd5b815167ffffffffffffffff80821115611e2f578283fd5b818401915084601f830112611e42578283fd5b815181811115611e5457611e5461230e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611e9a57611e9a61230e565b81604052828152876020848701011115611eb2578586fd5b611aa883602083016020880161224b565b600080600060608486031215611ed7578081fd5b833592506020840135611bc88161233d565b600080600080600060808688031215611f00578283fd5b853594506020860135611f128161233d565b9350611f2060408701611b4d565b9250606086013567ffffffffffffffff811115611f3b578182fd5b611f4788828901611b06565b969995985093965092949392505050565b8183823760009101908152919050565b60008251611f7a81846020870161224b565b9190910192915050565b6020815260008251806020840152611fa381604085016020870161224b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b7affffffffffffffffffffffffffffffffffffffffffffffffffffff8516815283602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612081578283fd5b83018035915067ffffffffffffffff82111561209b578283fd5b602001915036819003821315611b4657600080fd5b60007affffffffffffffffffffffffffffffffffffffffffffffffffffff8083168185168083038211156120e6576120e66122b0565b01949350505050565b60008219821115612102576121026122b0565b500190565b600082612116576121166122df565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561216a5761216a6122b0565b500590565b60008261217e5761217e6122df565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156121bb576121bb6122b0565b500290565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156121fa576121fa6122b0565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561222e5761222e6122b0565b50500390565b600082821015612246576122466122b0565b500390565b60005b8381101561226657818101518382015260200161224e565b83811115610ff25750506000910152565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156122a9576122a96122b0565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461235f57600080fd5b50565b801515811461235f57600080fdfea26469706673582212206a528b87b387a0e63acbb9965d107d2f038b0a8320974b8bb4bdf7d37d884fe964736f6c63430008040033

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  ]
[ 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.