ETH Price: $3,488.06 (+0.48%)
Gas: 6 Gwei

Contract

0xf3764eC89B1ad20A31ed633b1466363FAc1741c4
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw175636772023-06-26 12:44:11386 days ago1687783451IN
0xf3764eC8...FAc1741c4
0 ETH0.0008324813.26124144
Cancel Stream174005182023-06-03 13:41:11409 days ago1685799671IN
0xf3764eC8...FAc1741c4
0 ETH0.0013497626.12540252
Create Stream174005152023-06-03 13:40:35409 days ago1685799635IN
0xf3764eC8...FAc1741c4
0 ETH0.0019002326.31325053
Withdraw173620782023-05-29 3:48:47414 days ago1685332127IN
0xf3764eC8...FAc1741c4
0 ETH0.0022057627.61490574
Withdraw171655052023-05-01 10:52:35442 days ago1682938355IN
0xf3764eC8...FAc1741c4
0 ETH0.0049087261.45431119
Withdraw171363982023-04-27 8:41:23446 days ago1682584883IN
0xf3764eC8...FAc1741c4
0 ETH0.0023367637.22385974
Withdraw171363872023-04-27 8:39:11446 days ago1682584751IN
0xf3764eC8...FAc1741c4
0 ETH0.0021168233.72022081
Withdraw171281492023-04-26 4:53:35447 days ago1682484815IN
0xf3764eC8...FAc1741c4
0 ETH0.0019830331.58903613
Withdraw170975512023-04-21 21:54:47452 days ago1682114087IN
0xf3764eC8...FAc1741c4
0 ETH0.0028023344.64020289
Withdraw170885392023-04-20 15:18:35453 days ago1682003915IN
0xf3764eC8...FAc1741c4
0 ETH0.00800639100.23532621
Withdraw170736872023-04-18 12:52:47455 days ago1681822367IN
0xf3764eC8...FAc1741c4
0 ETH0.0039948850.01356617
Withdraw170734592023-04-18 12:06:35455 days ago1681819595IN
0xf3764eC8...FAc1741c4
0 ETH0.0025989241.3999359
Withdraw170373102023-04-13 7:52:23460 days ago1681372343IN
0xf3764eC8...FAc1741c4
0 ETH0.0015402124.53507659
Withdraw170371772023-04-13 7:22:47460 days ago1681370567IN
0xf3764eC8...FAc1741c4
0 ETH0.0015565324.79501907
Withdraw170289942023-04-12 2:33:23462 days ago1681266803IN
0xf3764eC8...FAc1741c4
0 ETH0.0013746621.89790729
Withdraw169823192023-04-05 11:35:47468 days ago1680694547IN
0xf3764eC8...FAc1741c4
0 ETH0.00223227.94332262
Withdraw169801542023-04-05 4:11:35468 days ago1680667895IN
0xf3764eC8...FAc1741c4
0 ETH0.0032749141
Withdraw169774672023-04-04 19:05:23469 days ago1680635123IN
0xf3764eC8...FAc1741c4
0 ETH0.0052804984.11644024
Withdraw169695472023-04-03 16:01:59470 days ago1680537719IN
0xf3764eC8...FAc1741c4
0 ETH0.0028142235.23244786
Withdraw169480492023-03-31 15:24:35473 days ago1680276275IN
0xf3764eC8...FAc1741c4
0 ETH0.0024855439.59383403
Withdraw169258542023-03-28 12:32:47476 days ago1680006767IN
0xf3764eC8...FAc1741c4
0 ETH0.0020199332.17681188
Withdraw168720802023-03-20 23:13:11484 days ago1679353991IN
0xf3764eC8...FAc1741c4
0 ETH0.0012494115.64432992
Withdraw168692942023-03-20 13:50:47484 days ago1679320247IN
0xf3764eC8...FAc1741c4
0 ETH0.0019348624.22332727
Withdraw168579072023-03-18 23:27:47486 days ago1679182067IN
0xf3764eC8...FAc1741c4
0 ETH0.0010776813.49194913
Withdraw168486972023-03-17 16:25:23487 days ago1679070323IN
0xf3764eC8...FAc1741c4
0 ETH0.0012946420.62326436
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
149952602022-06-20 7:11:07757 days ago1655709067  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.