Contract 0xF7adf6A1dF921B1aED77Ca4Bb17C5CE78c29C773

 

Contract Overview

Balance:
0 ETH

EtherValue:
$0.00
My Name Tag:
Not Available, login to update

ContractCreator:
GENESIS at txn GENESIS_f7adf6a1df921b1aed77ca4bb17c5ce78c29c773
Txn Hash Method
Block
From
To
Value
GENESIS_f7adf6a1df921b1aed77ca4bb17c5ce78c29c7730x6080604002021-01-14 15:51:401169 days 13 hrs agoGENESIS IN  Create: BathHouse0 ETH00
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Genesis Bytecode Match Only)

Contract Name:
BathHouse

Compiler Version
v0.7.6

Optimization Enabled:
Yes with 1 runs

Other Settings:
default evmVersion, GNU GPLv3 license
File 1 of 11 : ABDKMath64x64.sol
// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <[email protected]>
 */
pragma solidity =0.7.6; //<0.6.0-0||>=0.6.0 <0.7.0-0||>=0.7.0 <0.8.0-0;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */
library ABDKMath64x64 {
    /*
     * Minimum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

    /*
     * Maximum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    /**
     * Convert signed 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromInt(int256 x) internal pure returns (int128) {
        require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);
        return int128(x << 64);
    }

    /**
     * Convert signed 64.64 fixed point number into signed 64-bit integer number
     * rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64-bit integer number
     */
    function toInt(int128 x) internal pure returns (int64) {
        return int64(x >> 64);
    }

    /**
     * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromUInt(uint256 x) internal pure returns (int128) {
        require(x <= 0x7FFFFFFFFFFFFFFF);
        return int128(x << 64);
    }

    /**
     * Convert signed 64.64 fixed point number into unsigned 64-bit integer
     * number rounding down.  Revert on underflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return unsigned 64-bit integer number
     */
    function toUInt(int128 x) internal pure returns (uint64) {
        require(x >= 0);
        return uint64(x >> 64);
    }

    /**
     * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
     * number rounding down.  Revert on overflow.
     *
     * @param x signed 128.128-bin fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function from128x128(int256 x) internal pure returns (int128) {
        int256 result = x >> 64;
        require(result >= MIN_64x64 && result <= MAX_64x64);
        return int128(result);
    }

    /**
     * Convert signed 64.64 fixed point number into signed 128.128 fixed point
     * number.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 128.128 fixed point number
     */
    function to128x128(int128 x) internal pure returns (int256) {
        return int256(x) << 64;
    }

    /**
     * Calculate x + y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function add(int128 x, int128 y) internal pure returns (int128) {
        int256 result = int256(x) + y;
        require(result >= MIN_64x64 && result <= MAX_64x64);
        return int128(result);
    }

    /**
     * Calculate x - y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sub(int128 x, int128 y) internal pure returns (int128) {
        int256 result = int256(x) - y;
        require(result >= MIN_64x64 && result <= MAX_64x64);
        return int128(result);
    }

    /**
     * Calculate x * y rounding down.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function mul(int128 x, int128 y) internal pure returns (int128) {
        int256 result = (int256(x) * y) >> 64;
        require(result >= MIN_64x64 && result <= MAX_64x64);
        return int128(result);
    }

    /**
     * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
     * number and y is signed 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y signed 256-bit integer number
     * @return signed 256-bit integer number
     */
    function muli(int128 x, int256 y) internal pure returns (int256) {
        if (x == MIN_64x64) {
            require(
                y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
                    y <= 0x1000000000000000000000000000000000000000000000000
            );
            return -y << 63;
        } else {
            bool negativeResult = false;
            if (x < 0) {
                x = -x;
                negativeResult = true;
            }
            if (y < 0) {
                y = -y; // We rely on overflow behavior here
                negativeResult = !negativeResult;
            }
            uint256 absoluteResult = mulu(x, uint256(y));
            if (negativeResult) {
                require(
                    absoluteResult <=
                        0x8000000000000000000000000000000000000000000000000000000000000000
                );
                return -int256(absoluteResult); // We rely on overflow behavior here
            } else {
                require(
                    absoluteResult <=
                        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
                );
                return int256(absoluteResult);
            }
        }
    }

    /**
     * Calculate x * y rounding down, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y unsigned 256-bit integer number
     * @return unsigned 256-bit integer number
     */
    function mulu(int128 x, uint256 y) internal pure returns (uint256) {
        if (y == 0) return 0;

        require(x >= 0);

        uint256 lo = (uint256(x) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >>
            64;
        uint256 hi = uint256(x) * (y >> 128);

        require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        hi <<= 64;

        require(
            hi <=
                0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -
                    lo
        );
        return hi + lo;
    }

    /**
     * Calculate x / y rounding towards zero.  Revert on overflow or when y is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function div(int128 x, int128 y) internal pure returns (int128) {
        require(y != 0);
        int256 result = (int256(x) << 64) / y;
        require(result >= MIN_64x64 && result <= MAX_64x64);
        return int128(result);
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are signed 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x signed 256-bit integer number
     * @param y signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divi(int256 x, int256 y) internal pure returns (int128) {
        require(y != 0);

        bool negativeResult = false;
        if (x < 0) {
            x = -x; // We rely on overflow behavior here
            negativeResult = true;
        }
        if (y < 0) {
            y = -y; // We rely on overflow behavior here
            negativeResult = !negativeResult;
        }
        uint128 absoluteResult = divuu(uint256(x), uint256(y));
        if (negativeResult) {
            require(absoluteResult <= 0x80000000000000000000000000000000);
            return -int128(absoluteResult); // We rely on overflow behavior here
        } else {
            require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
            return int128(absoluteResult); // We rely on overflow behavior here
        }
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divu(uint256 x, uint256 y) internal pure returns (int128) {
        require(y != 0);
        uint128 result = divuu(x, y);
        require(result <= uint128(MAX_64x64));
        return int128(result);
    }

    /**
     * Calculate -x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function neg(int128 x) internal pure returns (int128) {
        require(x != MIN_64x64);
        return -x;
    }

    /**
     * Calculate |x|.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function abs(int128 x) internal pure returns (int128) {
        require(x != MIN_64x64);
        return x < 0 ? -x : x;
    }

    /**
     * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function inv(int128 x) internal pure returns (int128) {
        require(x != 0);
        int256 result = int256(0x100000000000000000000000000000000) / x;
        require(result >= MIN_64x64 && result <= MAX_64x64);
        return int128(result);
    }

    /**
     * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function avg(int128 x, int128 y) internal pure returns (int128) {
        return int128((int256(x) + int256(y)) >> 1);
    }

    /**
     * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
     * Revert on overflow or in case x * y is negative.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function gavg(int128 x, int128 y) internal pure returns (int128) {
        int256 m = int256(x) * int256(y);
        require(m >= 0);
        require(
            m <
                0x4000000000000000000000000000000000000000000000000000000000000000
        );
        return int128(sqrtu(uint256(m)));
    }

    /**
     * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y uint256 value
     * @return signed 64.64-bit fixed point number
     */
    function pow(int128 x, uint256 y) internal pure returns (int128) {
        uint256 absoluteResult;
        bool negativeResult = false;
        if (x >= 0) {
            absoluteResult = powu(uint256(x) << 63, y);
        } else {
            // We rely on overflow behavior here
            absoluteResult = powu(uint256(uint128(-x)) << 63, y);
            negativeResult = y & 1 > 0;
        }

        absoluteResult >>= 63;

        if (negativeResult) {
            require(absoluteResult <= 0x80000000000000000000000000000000);
            return -int128(absoluteResult); // We rely on overflow behavior here
        } else {
            require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
            return int128(absoluteResult); // We rely on overflow behavior here
        }
    }

    /**
     * Calculate sqrt (x) rounding down.  Revert if x < 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sqrt(int128 x) internal pure returns (int128) {
        require(x >= 0);
        return int128(sqrtu(uint256(x) << 64));
    }

    /**
     * Calculate binary logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function log_2(int128 x) internal pure returns (int128) {
        require(x > 0);

        int256 msb = 0;
        int256 xc = x;
        if (xc >= 0x10000000000000000) {
            xc >>= 64;
            msb += 64;
        }
        if (xc >= 0x100000000) {
            xc >>= 32;
            msb += 32;
        }
        if (xc >= 0x10000) {
            xc >>= 16;
            msb += 16;
        }
        if (xc >= 0x100) {
            xc >>= 8;
            msb += 8;
        }
        if (xc >= 0x10) {
            xc >>= 4;
            msb += 4;
        }
        if (xc >= 0x4) {
            xc >>= 2;
            msb += 2;
        }
        if (xc >= 0x2) msb += 1; // No need to shift xc anymore

        int256 result = (msb - 64) << 64;
        uint256 ux = uint256(x) << uint256(127 - msb);
        for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
            ux *= ux;
            uint256 b = ux >> 255;
            ux >>= 127 + b;
            result += bit * int256(b);
        }

        return int128(result);
    }

    /**
     * Calculate natural logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function ln(int128 x) internal pure returns (int128) {
        require(x > 0);

        return
            int128(
                (uint256(log_2(x)) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128
            );
    }

    /**
     * Calculate binary exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp_2(int128 x) internal pure returns (int128) {
        require(x < 0x400000000000000000); // Overflow

        if (x < -0x400000000000000000) return 0; // Underflow

        uint256 result = 0x80000000000000000000000000000000;

        if (x & 0x8000000000000000 > 0)
            result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128;
        if (x & 0x4000000000000000 > 0)
            result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128;
        if (x & 0x2000000000000000 > 0)
            result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128;
        if (x & 0x1000000000000000 > 0)
            result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128;
        if (x & 0x800000000000000 > 0)
            result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128;
        if (x & 0x400000000000000 > 0)
            result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128;
        if (x & 0x200000000000000 > 0)
            result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128;
        if (x & 0x100000000000000 > 0)
            result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128;
        if (x & 0x80000000000000 > 0)
            result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128;
        if (x & 0x40000000000000 > 0)
            result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128;
        if (x & 0x20000000000000 > 0)
            result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128;
        if (x & 0x10000000000000 > 0)
            result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128;
        if (x & 0x8000000000000 > 0)
            result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128;
        if (x & 0x4000000000000 > 0)
            result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128;
        if (x & 0x2000000000000 > 0)
            result = (result * 0x1000162E525EE054754457D5995292026) >> 128;
        if (x & 0x1000000000000 > 0)
            result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128;
        if (x & 0x800000000000 > 0)
            result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128;
        if (x & 0x400000000000 > 0)
            result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128;
        if (x & 0x200000000000 > 0)
            result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128;
        if (x & 0x100000000000 > 0)
            result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128;
        if (x & 0x80000000000 > 0)
            result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128;
        if (x & 0x40000000000 > 0)
            result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128;
        if (x & 0x20000000000 > 0)
            result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128;
        if (x & 0x10000000000 > 0)
            result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128;
        if (x & 0x8000000000 > 0)
            result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128;
        if (x & 0x4000000000 > 0)
            result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128;
        if (x & 0x2000000000 > 0)
            result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128;
        if (x & 0x1000000000 > 0)
            result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128;
        if (x & 0x800000000 > 0)
            result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128;
        if (x & 0x400000000 > 0)
            result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128;
        if (x & 0x200000000 > 0)
            result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128;
        if (x & 0x100000000 > 0)
            result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128;
        if (x & 0x80000000 > 0)
            result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128;
        if (x & 0x40000000 > 0)
            result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128;
        if (x & 0x20000000 > 0)
            result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128;
        if (x & 0x10000000 > 0)
            result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128;
        if (x & 0x8000000 > 0)
            result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128;
        if (x & 0x4000000 > 0)
            result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128;
        if (x & 0x2000000 > 0)
            result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128;
        if (x & 0x1000000 > 0)
            result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128;
        if (x & 0x800000 > 0)
            result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128;
        if (x & 0x400000 > 0)
            result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128;
        if (x & 0x200000 > 0)
            result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128;
        if (x & 0x100000 > 0)
            result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128;
        if (x & 0x80000 > 0)
            result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128;
        if (x & 0x40000 > 0)
            result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128;
        if (x & 0x20000 > 0)
            result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128;
        if (x & 0x10000 > 0)
            result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128;
        if (x & 0x8000 > 0)
            result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128;
        if (x & 0x4000 > 0)
            result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128;
        if (x & 0x2000 > 0)
            result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128;
        if (x & 0x1000 > 0)
            result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128;
        if (x & 0x800 > 0)
            result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128;
        if (x & 0x400 > 0)
            result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128;
        if (x & 0x200 > 0)
            result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128;
        if (x & 0x100 > 0)
            result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128;
        if (x & 0x80 > 0)
            result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128;
        if (x & 0x40 > 0)
            result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128;
        if (x & 0x20 > 0)
            result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128;
        if (x & 0x10 > 0)
            result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128;
        if (x & 0x8 > 0)
            result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128;
        if (x & 0x4 > 0)
            result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128;
        if (x & 0x2 > 0)
            result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128;
        if (x & 0x1 > 0)
            result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128;

        result >>= uint256(63 - (x >> 64));
        require(result <= uint256(MAX_64x64));

        return int128(result);
    }

    /**
     * Calculate natural exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp(int128 x) internal pure returns (int128) {
        require(x < 0x400000000000000000); // Overflow

        if (x < -0x400000000000000000) return 0; // Underflow

        return
            exp_2(
                int128((int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128)
            );
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return unsigned 64.64-bit fixed point number
     */
    function divuu(uint256 x, uint256 y) private pure returns (uint128) {
        require(y != 0);

        uint256 result;

        if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
            result = (x << 64) / y;
        else {
            uint256 msb = 192;
            uint256 xc = x >> 192;
            if (xc >= 0x100000000) {
                xc >>= 32;
                msb += 32;
            }
            if (xc >= 0x10000) {
                xc >>= 16;
                msb += 16;
            }
            if (xc >= 0x100) {
                xc >>= 8;
                msb += 8;
            }
            if (xc >= 0x10) {
                xc >>= 4;
                msb += 4;
            }
            if (xc >= 0x4) {
                xc >>= 2;
                msb += 2;
            }
            if (xc >= 0x2) msb += 1; // No need to shift xc anymore

            result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1);
            require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

            uint256 hi = result * (y >> 128);
            uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

            uint256 xh = x >> 192;
            uint256 xl = x << 64;

            if (xl < lo) xh -= 1;
            xl -= lo; // We rely on overflow behavior here
            lo = hi << 128;
            if (xl < lo) xh -= 1;
            xl -= lo; // We rely on overflow behavior here

            assert(xh == hi >> 128);

            result += xl / y;
        }

        require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return uint128(result);
    }

    /**
     * Calculate x^y assuming 0^0 is 1, where x is unsigned 129.127 fixed point
     * number and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x unsigned 129.127-bit fixed point number
     * @param y uint256 value
     * @return unsigned 129.127-bit fixed point number
     */
    function powu(uint256 x, uint256 y) private pure returns (uint256) {
        if (y == 0) return 0x80000000000000000000000000000000;
        else if (x == 0) return 0;
        else {
            int256 msb = 0;
            uint256 xc = x;
            if (xc >= 0x100000000000000000000000000000000) {
                xc >>= 128;
                msb += 128;
            }
            if (xc >= 0x10000000000000000) {
                xc >>= 64;
                msb += 64;
            }
            if (xc >= 0x100000000) {
                xc >>= 32;
                msb += 32;
            }
            if (xc >= 0x10000) {
                xc >>= 16;
                msb += 16;
            }
            if (xc >= 0x100) {
                xc >>= 8;
                msb += 8;
            }
            if (xc >= 0x10) {
                xc >>= 4;
                msb += 4;
            }
            if (xc >= 0x4) {
                xc >>= 2;
                msb += 2;
            }
            if (xc >= 0x2) msb += 1; // No need to shift xc anymore

            int256 xe = msb - 127;
            if (xe > 0) x >>= uint256(xe);
            else x <<= uint256(-xe);

            uint256 result = 0x80000000000000000000000000000000;
            int256 re = 0;

            while (y > 0) {
                if (y & 1 > 0) {
                    result = result * x;
                    y -= 1;
                    re += xe;
                    if (
                        result >=
                        0x8000000000000000000000000000000000000000000000000000000000000000
                    ) {
                        result >>= 128;
                        re += 1;
                    } else result >>= 127;
                    if (re < -127) return 0; // Underflow
                    require(re < 128); // Overflow
                } else {
                    x = x * x;
                    y >>= 1;
                    xe <<= 1;
                    if (
                        x >=
                        0x8000000000000000000000000000000000000000000000000000000000000000
                    ) {
                        x >>= 128;
                        xe += 1;
                    } else x >>= 127;
                    if (xe < -127) return 0; // Underflow
                    require(xe < 128); // Overflow
                }
            }

            if (re > 0) result <<= uint256(re);
            else if (re < 0) result >>= uint256(-re);

            return result;
        }
    }

    /**
     * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
     * number.
     *
     * @param x unsigned 256-bit integer number
     * @return unsigned 128-bit integer number
     */
    function sqrtu(uint256 x) private pure returns (uint128) {
        if (x == 0) return 0;
        else {
            uint256 xx = x;
            uint256 r = 1;
            if (xx >= 0x100000000000000000000000000000000) {
                xx >>= 128;
                r <<= 64;
            }
            if (xx >= 0x10000000000000000) {
                xx >>= 64;
                r <<= 32;
            }
            if (xx >= 0x100000000) {
                xx >>= 32;
                r <<= 16;
            }
            if (xx >= 0x10000) {
                xx >>= 16;
                r <<= 8;
            }
            if (xx >= 0x100) {
                xx >>= 8;
                r <<= 4;
            }
            if (xx >= 0x10) {
                xx >>= 4;
                r <<= 2;
            }
            if (xx >= 0x8) {
                r <<= 1;
            }
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1; // Seven iterations should be enough
            uint256 r1 = x / r;
            return uint128(r < r1 ? r : r1);
        }
    }
}

File 2 of 11 : BathPair.sol
// SPDX-License-Identifier: BUSL-1.1

/// @author Benjamin Hughes - Rubicon
/// @notice This contract allows a strategist to use user funds in order to market make for a Rubicon pair
/// @notice The BathPair is the admin for the pair's liquidity and has many security checks in place
/// @notice This contract is also where strategists claim rewards for successful market making

pragma solidity =0.7.6;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./BathToken.sol";
import "./BathHouse.sol";
import "../RubiconMarket.sol";
import "../peripheral_contracts/ABDKMath64x64.sol";

contract BathPair {
    using SafeMath for uint256;

    address public bathHouse;
    address public underlyingAsset;
    address public underlyingQuote;

    address public bathAssetAddress;
    address public bathQuoteAddress;

    address public RubiconMarketAddress;

    bool public initialized;

    int128 internal shapeCoefNum;
    uint256 public maxOrderSizeBPS;

    /// @dev Internal variables for localalized searching in bathScrub
    uint256 internal start;
    uint256 internal searchRadius;

    uint256 internal totalAssetFills;
    uint256 internal totalQuoteFills;

    /// @dev [askID, ask.pay_amt, bidID, bid.pay_amt, timestamp]
    StrategistTrade[] public outstandingPairIDs;

    /// @dev Maps a trade ID to each of their strategists for rewards purposes
    mapping(address => uint256) public strategist2FillsAsset;
    mapping(address => uint256) public strategist2FillsQuote;

    struct order {
        uint256 pay_amt;
        ERC20 pay_gem;
        uint256 buy_amt;
        ERC20 buy_gem;
    }

    struct StrategistTrade {
        uint256 askId;
        uint256 askAmt;
        uint256 bidId;
        uint256 bidAmt;
        uint256 timestamp;
        address strategist;
    }

    event LogStrategistTrade(
        uint256 askId,
        uint256 askAmt,
        uint256 bidId,
        uint256 bidAmt,
        uint256 timestamp,
        address strategist
    );

    event StrategistRewardClaim(
        address strategist,
        address asset,
        uint256 amountOfReward,
        uint256 timestamp
    );

    /// @dev Proxy-safe initialization of storage
    function initialize(
        address _bathAssetAddress,
        address _bathQuoteAddress,
        address _bathHouse,
        uint256 _maxOrderSizeBPS,
        int128 _shapeCoefNum
    ) external {
        require(!initialized);
        bathHouse = _bathHouse;

        bathAssetAddress = _bathAssetAddress;
        bathQuoteAddress = _bathQuoteAddress;

        require(
            BathToken(bathAssetAddress).underlying() !=
                address(0x0000000000000000000000000000000000000000)
        );
        require(
            BathToken(bathQuoteAddress).underlying() !=
                address(0x0000000000000000000000000000000000000000)
        );
        underlyingAsset = BathToken(bathAssetAddress).underlying();
        underlyingQuote = BathToken(bathQuoteAddress).underlying();

        require(
            BathHouse(bathHouse).getMarket() !=
                address(0x0000000000000000000000000000000000000000)
        );
        RubiconMarketAddress = BathHouse(bathHouse).getMarket();

        maxOrderSizeBPS = _maxOrderSizeBPS;
        shapeCoefNum = _shapeCoefNum;
        start = 0;
        searchRadius = 2;
        initialized = true;
    }

    modifier onlyBathHouse {
        require(msg.sender == bathHouse);
        _;
    }

    modifier onlyApprovedStrategist(address targetStrategist) {
        require(
            BathHouse(bathHouse).isApprovedStrategist(targetStrategist) == true,
            "you are not an approved strategist - bathPair"
        );
        _;
    }

    modifier enforceReserveRatio {
        _;
        require(
            (
                BathToken(bathAssetAddress).totalSupply().mul(
                    BathHouse(bathHouse).reserveRatio()
                )
            )
            .div(100) <= IERC20(underlyingAsset).balanceOf(bathAssetAddress)
        );
        require(
            (
                BathToken(bathQuoteAddress).totalSupply().mul(
                    BathHouse(bathHouse).reserveRatio()
                )
            )
            .div(100) <= IERC20(underlyingQuote).balanceOf(bathQuoteAddress)
        );
    }

    function setMaxOrderSizeBPS(uint16 val) external onlyBathHouse {
        maxOrderSizeBPS = val;
    }

    function setShapeCoefNum(int128 val) external onlyBathHouse {
        shapeCoefNum = val;
    }

    function setSearchRadius(uint256 val) external onlyBathHouse {
        searchRadius = val;
    }

    function getThisBathQuote() external view returns (address) {
        require(initialized);
        return bathQuoteAddress;
    }

    function getThisBathAsset() external view returns (address) {
        require(initialized);
        return bathAssetAddress;
    }

    function getOutstandingPairCount() external view returns (uint256) {
        return outstandingPairIDs.length;
    }

    function getSearchRadius() external view returns (uint256) {
        return searchRadius;
    }

    // ** Internal Functions **
    // Takes the proposed bid and ask as a parameter - that the offers placed won't match and are maker orders

    function getMidpointPrice() internal view returns (int128) {
        address _RubiconMarketAddress = RubiconMarketAddress;
        uint256 bestAskID = RubiconMarket(_RubiconMarketAddress).getBestOffer(
            ERC20(underlyingAsset),
            ERC20(underlyingQuote)
        );
        uint256 bestBidID = RubiconMarket(_RubiconMarketAddress).getBestOffer(
            ERC20(underlyingQuote),
            ERC20(underlyingAsset)
        );
        // Throw a zero if unable to determine midpoint from the book
        if (bestAskID == 0 || bestBidID == 0) {
            return 0;
        }
        order memory bestAsk = getOfferInfo(bestAskID);
        order memory bestBid = getOfferInfo(bestBidID);
        int128 midpoint = ABDKMath64x64.divu(
            (
                (bestAsk.buy_amt.div(bestAsk.pay_amt)).add(
                    bestBid.pay_amt.div(bestBid.buy_amt)
                )
            ),
            2
        );
        return midpoint;
    }

    // Returns filled liquidity to the correct bath pool
    function rebalancePair(
        address _bathHouse,
        address _underlyingAsset,
        address _underlyingQuote
    ) internal {
        address _bathAssetAddress = bathAssetAddress;
        address _bathQuoteAddress = bathQuoteAddress;
        uint256 bathAssetYield = ERC20(_underlyingQuote).balanceOf(
            _bathAssetAddress
        );
        uint256 bathQuoteYield = ERC20(_underlyingAsset).balanceOf(
            _bathQuoteAddress
        );
        uint16 stratReward = BathHouse(_bathHouse).getBPSToStrats(
            address(this)
        );
        if (bathAssetYield > 0) {
            BathToken(_bathAssetAddress).rebalance(
                _bathQuoteAddress,
                _underlyingQuote,
                stratReward
            );
        }

        if (bathQuoteYield > 0) {
            BathToken(_bathQuoteAddress).rebalance(
                _bathAssetAddress,
                _underlyingAsset,
                stratReward
            );
        }
    }

    // orderID of the fill
    // only log fills for each strategist - needs to be asset specific
    // isAssetFill are *quotes* that result in asset yield
    function logFill(
        uint256 amt,
        bool isAssetFill,
        address sender
    ) internal {
        // Goal is to map a fill to a strategist
        if (isAssetFill) {
            strategist2FillsAsset[sender] += amt;
            totalAssetFills += amt;
        } else {
            strategist2FillsQuote[sender] += amt;
            totalQuoteFills += amt;
        }
    }

    function removeElement(uint256 index) internal {
        outstandingPairIDs[index] = outstandingPairIDs[
            outstandingPairIDs.length - 1
        ];
        outstandingPairIDs.pop();
    }

    function cancelPartialFills() internal {
        uint256 timeDelay = BathHouse(bathHouse).timeDelay();
        uint256 len = outstandingPairIDs.length;
        uint256 _start = start;

        uint256 _searchRadius = searchRadius;
        if (_start + _searchRadius >= len) {
            // start over from beggining
            if (_searchRadius >= len) {
                _start = 0;
                _searchRadius = len;
            } else {
                _searchRadius = len - _start;
            }
        }

        for (uint256 x = _start; x < _start + _searchRadius; x++) {
            if (
                outstandingPairIDs[x].timestamp < (block.timestamp - timeDelay)
            ) {
                StrategistTrade memory info = outstandingPairIDs[x];
                order memory offer1 = getOfferInfo(info.askId); //ask
                order memory offer2 = getOfferInfo(info.bidId); //bid
                uint256 askDelta = info.askAmt - offer1.pay_amt;
                uint256 bidDelta = info.bidAmt - offer2.pay_amt;

                // if real
                if (info.askId != 0) {
                    // if delta > 0 - delta is fill => handle any amount of fill here
                    if (askDelta > 0) {
                        logFill(askDelta, true, info.strategist);
                        BathToken(bathAssetAddress).removeFilledTradeAmount(
                            askDelta
                        );
                        // not a full fill
                        if (askDelta != info.askAmt) {
                            BathToken(bathAssetAddress).cancel(
                                info.askId,
                                info.askAmt.sub(askDelta)
                            );
                        }
                    }
                    // otherwise didn't fill so cancel
                    else {
                        BathToken(bathAssetAddress).cancel(
                            info.askId,
                            info.askAmt
                        ); // pas amount too
                    }
                }

                // if real
                if (info.bidId != 0) {
                    // if delta > 0 - delta is fill => handle any amount of fill here
                    if (bidDelta > 0) {
                        logFill(bidDelta, false, info.strategist);
                        BathToken(bathQuoteAddress).removeFilledTradeAmount(
                            bidDelta
                        );
                        // not a full fill
                        if (bidDelta != info.bidAmt) {
                            BathToken(bathQuoteAddress).cancel(
                                info.bidId,
                                info.bidAmt.sub(bidDelta)
                            );
                        }
                    }
                    // otherwise didn't fill so cancel
                    else {
                        BathToken(bathQuoteAddress).cancel(
                            info.bidId,
                            info.bidAmt
                        ); // pass amount too
                    }
                }

                removeElement(x);
                x--;
                _searchRadius--;
            }
        }
        if (_start + searchRadius >= len) {
            start = 0;
        } else {
            start = _start + searchRadius;
        }
    }

    // Get offer info from Rubicon Market
    function getOfferInfo(uint256 id) internal view returns (order memory) {
        (
            uint256 ask_amt,
            ERC20 ask_gem,
            uint256 bid_amt,
            ERC20 bid_gem
        ) = RubiconMarket(RubiconMarketAddress).getOffer(id);
        order memory offerInfo = order(ask_amt, ask_gem, bid_amt, bid_gem);
        return offerInfo;
    }

    // ** External functions that can be called by Strategists **
    function executeStrategy(
        uint256 askNumerator, // Quote / Asset
        uint256 askDenominator, // Asset / Quote
        uint256 bidNumerator, // size in ASSET
        uint256 bidDenominator // size in QUOTES
    ) external enforceReserveRatio onlyApprovedStrategist(msg.sender) {
        // Require at least one order is non-zero
        require(
            (askNumerator > 0 && askDenominator > 0) ||
                (bidNumerator > 0 && bidDenominator > 0)
        );
        address _underlyingAsset = underlyingAsset;
        address _underlyingQuote = underlyingQuote;
        address _bathAssetAddress = bathAssetAddress;
        address _bathQuoteAddress = bathQuoteAddress;
        address _bathHouse = bathHouse;

        // Enforce dynamic ordersizing and inventory management
        require(
            askNumerator <=
                getMaxOrderSize(_underlyingAsset, _bathAssetAddress),
            "ask too large"
        );
        require(
            bidNumerator <=
                getMaxOrderSize(_underlyingQuote, _bathQuoteAddress),
            "bid too large"
        );

        // Enforce that the bath is scrubbed for outstanding pairs
        require(
            outstandingPairIDs.length <
                BathHouse(_bathHouse).maxOutstandingPairCount(),
            "too many outstanding pairs, please call bathScrub() first"
        );
        require(
            (askNumerator > 0 && askDenominator > 0) ||
                (bidNumerator > 0 && bidDenominator > 0),
            "one order must be non-zero"
        );

        // Calculate new bid and/or ask
        order memory ask = order(
            askNumerator,
            ERC20(_underlyingAsset),
            askDenominator,
            ERC20(_underlyingQuote)
        );
        order memory bid = order(
            bidNumerator,
            ERC20(underlyingQuote),
            bidDenominator,
            ERC20(underlyingAsset)
        );

        // Place new bid and/or ask
        uint256 newAskID = BathToken(bathAssetAddress).placeOffer(
            ask.pay_amt,
            ask.pay_gem,
            ask.buy_amt,
            ask.buy_gem
        );

        uint256 newBidID = BathToken(bathQuoteAddress).placeOffer(
            bid.pay_amt,
            bid.pay_gem,
            bid.buy_amt,
            bid.buy_gem
        );

        // Strategist trade is recorded so they can get paid and the trade is logged for time
        StrategistTrade memory outgoing = StrategistTrade(
            newAskID,
            ask.pay_amt,
            newBidID,
            bid.pay_amt,
            block.timestamp,
            msg.sender
        );
        outstandingPairIDs.push(outgoing);

        emit LogStrategistTrade(
            outgoing.askId,
            outgoing.askAmt,
            outgoing.bidId,
            outgoing.bidAmt,
            outgoing.timestamp,
            outgoing.strategist
        );

        // Return any filled yield to the appropriate bathToken/liquidity pool
        rebalancePair(_bathHouse, _underlyingAsset, _underlyingQuote);
    }

    // This function cleans outstanding orders and rebalances yield between bathTokens
    function bathScrub() external {
        // Cancel Outstanding Orders that need to be cleared or logged for yield
        cancelPartialFills();
    }

    // Return the largest order size that can be placed as a strategist for given asset and liquidity pool
    function getMaxOrderSize(address asset, address bathTokenAddress)
        public
        view
        returns (uint256 maxOrderSize)
    {
        address _underlyingAsset = underlyingAsset;
        address _underlyingQuote = underlyingQuote;
        require(asset == _underlyingAsset || asset == _underlyingQuote);
        int128 shapeCoef = ABDKMath64x64.div(shapeCoefNum, 1000);

        uint256 underlyingBalance = IERC20(asset).balanceOf(bathTokenAddress);
        require(
            underlyingBalance > 0,
            "no bathToken liquidity to calculate max orderSize permissable"
        );

        // If no midpoint in book, allow **permissioned** strategist to maxOrderSize
        int128 midpoint = getMidpointPrice();
        if (midpoint == 0) {
            return maxOrderSizeBPS.mul(underlyingBalance).div(10000);
        }
        // if the asset/quote is overweighted: underlyingBalance / (Proportion of quote allocated to pair) * underlyingQuote balance
        if (asset == _underlyingAsset) {
            int128 ratio = ABDKMath64x64.divu(
                underlyingBalance,
                IERC20(_underlyingQuote).balanceOf(bathQuoteAddress)
            );
            if (ABDKMath64x64.mul(ratio, midpoint) > (2**64)) {
                // bid at maxSize
                return maxOrderSizeBPS.mul(underlyingBalance).div(10000);
            } else {
                // return dynamic order size
                uint256 maxSize = maxOrderSizeBPS.mul(underlyingBalance).div(
                    10000
                );
                int128 shapeFactor = ABDKMath64x64.exp(
                    ABDKMath64x64.mul(
                        shapeCoef,
                        ABDKMath64x64.inv(ABDKMath64x64.mul(ratio, midpoint))
                    )
                );
                uint256 dynamicSize = ABDKMath64x64.mulu(shapeFactor, maxSize);
                return dynamicSize;
            }
        } else if (asset == _underlyingQuote) {
            int128 ratio = ABDKMath64x64.divu(
                underlyingBalance,
                IERC20(_underlyingAsset).balanceOf(bathAssetAddress)
            );
            if (ABDKMath64x64.div(ratio, midpoint) > (2**64)) {
                return maxOrderSizeBPS.mul(underlyingBalance).div(10000);
            } else {
                // return dynamic order size
                uint256 maxSize = maxOrderSizeBPS.mul(underlyingBalance).div(
                    10000
                );
                int128 shapeFactor = ABDKMath64x64.exp(
                    ABDKMath64x64.mul(
                        shapeCoef,
                        ABDKMath64x64.inv(ABDKMath64x64.div(ratio, midpoint))
                    )
                );
                uint256 dynamicSize = ABDKMath64x64.mulu(shapeFactor, maxSize);
                return dynamicSize;
            }
        }
    }

    // This function allows a strategist to remove Pools liquidity from the order book
    function removeLiquidity(uint256 id) external {
        order memory ord = getOfferInfo(id);
        if (ord.pay_gem == ERC20(underlyingAsset)) {
            uint256 len = outstandingPairIDs.length;
            for (uint256 x = 0; x < len; x++) {
                if (outstandingPairIDs[x].askId == id) {
                    require(
                        msg.sender == outstandingPairIDs[x].strategist,
                        "only strategist can cancel their orders"
                    );
                    BathToken(bathAssetAddress).cancel(
                        id,
                        outstandingPairIDs[x].askAmt
                    );
                    removeElement(x);
                    break;
                }
            }
        } else if (ord.pay_gem == ERC20(underlyingQuote)) {
            uint256 len = outstandingPairIDs.length;
            for (uint256 x = 0; x < len; x++) {
                if (outstandingPairIDs[x].bidId == id) {
                    require(
                        msg.sender == outstandingPairIDs[x].strategist,
                        "only strategist can cancel their orders"
                    );
                    BathToken(bathAssetAddress).cancel(
                        id,
                        outstandingPairIDs[x].bidAmt
                    );
                    removeElement(x);
                    break;
                }
            }
        }
    }

    // function where strategists claim rewards proportional to their quantity of fills
    function strategistBootyClaim() external {
        uint256 fillCountA = strategist2FillsAsset[msg.sender];
        uint256 fillCountQ = strategist2FillsQuote[msg.sender];
        if (fillCountA > 0) {
            uint256 booty = (
                fillCountA.mul(ERC20(underlyingAsset).balanceOf(address(this)))
            )
            .div(totalAssetFills);
            IERC20(underlyingAsset).transfer(msg.sender, booty);
            emit StrategistRewardClaim(
                msg.sender,
                underlyingAsset,
                booty,
                block.timestamp
            );
            totalAssetFills -= fillCountA;
            strategist2FillsAsset[msg.sender] -= fillCountA;
        }
        if (fillCountQ > 0) {
            uint256 booty = (
                fillCountQ.mul(ERC20(underlyingQuote).balanceOf(address(this)))
            )
            .div(totalQuoteFills);
            IERC20(underlyingQuote).transfer(msg.sender, booty);
            emit StrategistRewardClaim(
                msg.sender,
                underlyingQuote,
                booty,
                block.timestamp
            );
            totalQuoteFills -= fillCountQ;
            strategist2FillsQuote[msg.sender] -= fillCountQ;
        }
    }
}

File 3 of 11 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, 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 4 of 11 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 5 of 11 : BathToken.sol
// SPDX-License-Identifier: BUSL-1.1

/// @author Benjamin Hughes - Rubicon
/// @notice This contract represents a single-asset liquidity pool for Rubicon Pools
/// @notice Any user can deposit assets into this pool and earn yield from successful strategist market making with their liquidity
/// @notice This contract looks to both BathPairs and the BathHouse as its admin

pragma solidity =0.7.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../RubiconMarket.sol";
import "./BathHouse.sol";

contract BathToken {
    using SafeMath for uint256;
    bool public initialized;

    string public symbol;
    string public name;
    uint8 public decimals;

    address public RubiconMarketAddress;
    address public bathHouse; // admin
    address public feeTo;
    IERC20 public underlyingToken;
    uint256 public feeBPS;

    uint256 public totalSupply;
    uint256 public outstandingAmount; // quantity of underlyingToken that is in the orderbook and still in pools
    uint256[] outstandingIDs;
    mapping(uint256 => uint256) id2Ind;

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

    bytes32 public DOMAIN_SEPARATOR;
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
    mapping(address => uint256) public nonces;

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    event Transfer(address indexed from, address indexed to, uint256 value);
    event LogInit(uint256 timeOfInit);
    event Deposit(
        uint256 depositedAmt,
        IERC20 asset,
        uint256 sharesReceived,
        address depositor
    );

    /// @dev Proxy-safe initialization of storage
    function initialize(
        string memory bathName,
        IERC20 token,
        address market,
        address _bathHouse,
        address _feeTo
    ) external {
        require(!initialized);
        symbol = bathName;
        underlyingToken = token;
        RubiconMarketAddress = market;
        bathHouse = _bathHouse;

        uint256 chainId;
        assembly {
            chainId := chainid()
        }
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                chainId,
                address(this)
            )
        );
        name = "BathToken v1";
        decimals = 18;

        require(
            RubiconMarket(RubiconMarketAddress).initialized() &&
                BathHouse(bathHouse).initialized()
        );

        // Add infinite approval of Rubicon Market for this asset

        IERC20(address(token)).approve(RubiconMarketAddress, 2**256 - 1);
        emit LogInit(block.timestamp);

        feeTo = _feeTo; //BathHouse admin is initial recipient
        feeBPS = 0; //Fee set to zero

        initialized = true;
    }

    modifier onlyPair {
        require(
            BathHouse(bathHouse).isApprovedPair(msg.sender) == true,
            "not an approved pair - bathToken"
        );
        _;
    }

    function setMarket(address newRubiconMarket) external {
        require(msg.sender == bathHouse && initialized);
        RubiconMarketAddress = newRubiconMarket;
    }

    function setBathHouse(address newBathHouse) external {
        require(msg.sender == bathHouse && initialized);
        bathHouse = newBathHouse;
    }

    function setFeeBPS(uint256 _feeBPS) external {
        require(msg.sender == bathHouse && initialized);
        feeBPS = _feeBPS;
    }

    function setFeeTo(address _feeTo) external {
        require(msg.sender == bathHouse && initialized);
        feeTo = _feeTo;
    }

    function underlying() external view returns (address) {
        require(initialized);
        return address(underlyingToken);
    }

    /// @notice returns the amount of underlying ERC20 tokens in this pool in addition to
    ///         any tokens that may be outstanding in the Rubicon order book
    function underlyingBalance() public view returns (uint256) {
        require(initialized, "BathToken not initialized");

        uint256 _pool = IERC20(underlyingToken).balanceOf(address(this));
        return _pool.add(outstandingAmount);
    }

    // ** Rubicon Market Functions: **
    function cancel(uint256 id, uint256 amt) external onlyPair {
        outstandingAmount = outstandingAmount.sub(amt);
        RubiconMarket(RubiconMarketAddress).cancel(id);
    }

    function removeFilledTradeAmount(uint256 amt) external onlyPair {
        outstandingAmount = outstandingAmount.sub(amt);
    }

    // function that places a bid/ask in the orderbook for a given pair
    function placeOffer(
        uint256 pay_amt,
        ERC20 pay_gem,
        uint256 buy_amt,
        ERC20 buy_gem
    ) external onlyPair returns (uint256) {
        // Place an offer in RubiconMarket
        // If incomplete offer return 0
        if (
            pay_amt == 0 ||
            pay_gem == ERC20(0) ||
            buy_amt == 0 ||
            buy_gem == ERC20(0)
        ) {
            return 0;
        }

        uint256 id = RubiconMarket(RubiconMarketAddress).offer(
            pay_amt,
            pay_gem,
            buy_amt,
            buy_gem,
            0,
            false
        );
        outstandingAmount = outstandingAmount.add(pay_amt);
        return (id);
    }

    // This function returns filled orders to the correct liquidity pool and sends strategist rewards to the Pair
    function rebalance(
        address sisterBath,
        address underlyingAsset, /* sister asset */
        uint256 stratProportion
    ) external onlyPair {
        require(initialized);
        uint256 stratReward = (
            stratProportion.mul(
                IERC20(underlyingAsset).balanceOf(address(this))
            )
        )
        .div(10000);
        IERC20(underlyingAsset).transfer(
            sisterBath,
            IERC20(underlyingAsset).balanceOf(address(this)).sub(stratReward)
        );
        IERC20(underlyingAsset).transfer(msg.sender, stratReward);
    }

    // ** User Entrypoints: **
    // https://github.com/yearn/yearn-protocol/blob/develop/contracts/vaults/yVault.sol - shoutout yEarn homies
    function deposit(uint256 _amount) external {
        uint256 _pool = underlyingBalance();
        uint256 _before = underlyingToken.balanceOf(address(this));

        underlyingToken.transferFrom(msg.sender, address(this), _amount);
        uint256 _after = underlyingToken.balanceOf(address(this));
        _amount = _after.sub(_before); // Additional check for deflationary tokens

        uint256 shares = 0;
        if (totalSupply == 0) {
            shares = _amount;
        } else {
            shares = (_amount.mul(totalSupply)).div(_pool);
        }
        _mint(msg.sender, shares);
        emit Deposit(_amount, underlyingToken, shares, msg.sender);
    }

    // No rebalance implementation for lower fees and faster swaps
    function withdraw(uint256 _shares) external {
        uint256 r = (underlyingBalance().mul(_shares)).div(totalSupply);
        _burn(msg.sender, _shares);
        uint256 _fee = r.mul(feeBPS).div(10000);
        IERC20(underlyingToken).transfer(feeTo, _fee);
        underlyingToken.transfer(msg.sender, r.sub(_fee));
    }

    // ** Internal Functions **

    function _mint(address to, uint256 value) internal {
        totalSupply = totalSupply.add(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(address(0), to, value);
    }

    function _burn(address from, uint256 value) internal {
        balanceOf[from] = balanceOf[from].sub(value);
        totalSupply = totalSupply.sub(value);
        emit Transfer(from, address(0), value);
    }

    function _approve(
        address owner,
        address spender,
        uint256 value
    ) private {
        allowance[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    function _transfer(
        address from,
        address to,
        uint256 value
    ) private {
        balanceOf[from] = balanceOf[from].sub(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(from, to, value);
    }

    function approve(address spender, uint256 value) external returns (bool) {
        _approve(msg.sender, spender, value);
        return true;
    }

    function transfer(address to, uint256 value) external returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool) {
        if (allowance[from][msg.sender] != uint256(-1)) {
            allowance[from][msg.sender] = allowance[from][msg.sender].sub(
                value
            );
        }
        _transfer(from, to, value);
        return true;
    }

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(deadline >= block.timestamp, "bathToken: EXPIRED");
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        PERMIT_TYPEHASH,
                        owner,
                        spender,
                        value,
                        nonces[owner]++,
                        deadline
                    )
                )
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(
            recoveredAddress != address(0) && recoveredAddress == owner,
            "bathToken: INVALID_SIGNATURE"
        );
        _approve(owner, spender, value);
    }
}

File 6 of 11 : BathHouse.sol
// SPDX-License-Identifier: BUSL-1.1

/// @author Benjamin Hughes - Rubicon
/// @notice This contract acts as the admin for the Rubicon Pools system
/// @notice The BathHouse approves library contracts and initializes bathPairs

pragma solidity =0.7.6;

import "./BathPair.sol";

contract BathHouse {
    string public name;

    mapping(address => mapping(address => address)) public getPair;

    address public admin;
    address public RubiconMarketAddress;

    // List of approved strategies
    mapping(address => bool) public approvedBathTokens;
    mapping(address => bool) public approvedPairs;
    mapping(address => bool) public approvedStrategists;

    mapping(address => bool) internal bathQuoteExists;
    mapping(address => bool) internal bathAssetExists;
    mapping(address => uint8) public propToStrategists;
    mapping(address => address) internal quoteToBathQuote;
    mapping(address => address) internal assetToBathAsset;

    bool public initialized;
    bool public permissionedStrategists; //if true strategists are permissioned

    // Key, system-wide risk parameters for Pools
    uint256 public reserveRatio; // proportion of the pool that must remain present in the pair

    // The delay after which unfilled orders are cancelled
    uint256 public timeDelay;

    // Constraint variable for the max amount of outstanding market making pairs at a time
    uint256 public maxOutstandingPairCount;

    /// @dev Proxy-safe initialization of storage
    function initialize(
        address market,
        uint256 _reserveRatio,
        uint256 _timeDelay,
        uint256 mopc
    ) external {
        require(!initialized);
        name = "Rubicon Bath House";
        admin = msg.sender;
        timeDelay = _timeDelay;
        require(_reserveRatio <= 100);
        require(_reserveRatio > 0);
        reserveRatio = _reserveRatio;

        maxOutstandingPairCount = mopc;

        RubiconMarketAddress = market;
        approveStrategist(admin);
        permissionedStrategists = true;
        initialized = true;
    }

    function initBathPair(
        address asset,
        address quote,
        address pair,
        uint8 _propToStrategists
    ) external onlyAdmin returns (address newPair) {
        //calls initialize on two Bath Tokens and spins them up
        require(asset != quote);
        require(asset != address(0));
        require(quote != address(0));

        // Ensure the pair doesn't exist and approved
        require(!isApprovedPair(getPair[asset][quote]));
        propToStrategists[pair] = _propToStrategists;

        getPair[asset][quote] = address(pair);

        approvePair(address(pair));
        addQuote(quote, BathPair(pair).getThisBathQuote());
        addAsset(asset, BathPair(pair).getThisBathAsset());
        return address(pair);
    }

    modifier onlyAdmin {
        require(msg.sender == admin);
        _;
    }

    function setBathHouseAdmin(address newAdmin) external onlyAdmin {
        admin = newAdmin;
    }

    function setPermissionedStrategists(bool _new) external onlyAdmin {
        permissionedStrategists = _new;
    }

    // Setter Functions for paramters - onlyAdmin
    function setCancelTimeDelay(uint256 value) external onlyAdmin {
        timeDelay = value;
    }

    function setReserveRatio(uint256 rr) external onlyAdmin {
        require(rr <= 100);
        require(rr > 0);
        reserveRatio = rr;
    }

    function setPropToStrats(uint8 value, address pair) external onlyAdmin {
        require(value < 50);
        require(value >= 0);
        propToStrategists[pair] = value;
    }

    function setMaxOutstandingPairCount(uint256 value) external onlyAdmin {
        maxOutstandingPairCount = value;
    }

    function setBathTokenMarket(address bathToken, address newMarket)
        external
        onlyAdmin
    {
        BathToken(bathToken).setMarket(newMarket);
    }

    function setBathTokenBathHouse(address bathToken, address newAdmin)
        external
        onlyAdmin
    {
        BathToken(bathToken).setMarket(newAdmin);
    }

    function setBathTokenFeeBPS(address bathToken, uint256 newBPS)
        external
        onlyAdmin
    {
        BathToken(bathToken).setFeeBPS(newBPS);
    }

    function setFeeTo(address bathToken, address feeTo) external onlyAdmin {
        BathToken(bathToken).setFeeTo(feeTo);
    }

    function setBathPairMOSBPS(address bathPair, uint16 mosbps)
        external
        onlyAdmin
    {
        BathPair(bathPair).setMaxOrderSizeBPS(mosbps);
    }

    function setBathPairSearchRadius(address bathPair, uint256 sr)
        external
        onlyAdmin
    {
        BathPair(bathPair).setSearchRadius(sr);
    }

    function setBathPairSCN(address bathPair, int128 val) external onlyAdmin {
        BathPair(bathPair).setShapeCoefNum(val);
    }

    function setMarket(address newMarket) external onlyAdmin {
        RubiconMarketAddress = newMarket;
    }

    // Getter Functions for parameters
    function getMarket() external view returns (address) {
        return RubiconMarketAddress;
    }

    function getReserveRatio() external view returns (uint256) {
        return reserveRatio;
    }

    function getCancelTimeDelay() external view returns (uint256) {
        return timeDelay;
    }

    function getBathPair(address asset, address quote)
        external
        view
        returns (address pair)
    {
        return getPair[asset][quote];
    }

    function doesQuoteExist(address quote) external view returns (bool) {
        return bathQuoteExists[quote];
    }

    function doesAssetExist(address asset) external view returns (bool) {
        return bathAssetExists[asset];
    }

    function getBathTokenfromQuote(address quote)
        external
        view
        returns (address)
    {
        return quoteToBathQuote[quote];
    }

    function getBathTokenfromAsset(address asset)
        external
        view
        returns (address)
    {
        return assetToBathAsset[asset];
    }

    function getBPSToStrats(address pair) external view returns (uint8) {
        return propToStrategists[pair];
    }

    // ** Security Checks used throughout Pools **
    function isApprovedStrategist(address wouldBeStrategist)
        external
        view
        returns (bool)
    {
        if (
            approvedStrategists[wouldBeStrategist] == true ||
            !permissionedStrategists
        ) {
            return true;
        } else {
            return false;
        }
    }

    function isApprovedPair(address pair) public view returns (bool) {
        if (approvedPairs[pair] == true) {
            return true;
        } else {
            return false;
        }
    }

    function approveStrategist(address strategist) public onlyAdmin {
        approvedStrategists[strategist] = true;
    }

    function approvePair(address pair) internal {
        approvedPairs[pair] = true;
    }

    function removePair(address pair) external onlyAdmin {
        approvedPairs[pair] = false;
    }

    function addQuote(address quote, address bathQuote) internal {
        if (bathQuoteExists[quote]) {
            return;
        } else {
            bathQuoteExists[quote] = true;
            quoteToBathQuote[quote] = bathQuote;
        }
    }

    function addAsset(address asset, address bathAsset) internal {
        if (bathAssetExists[asset]) {
            return;
        } else {
            bathAssetExists[asset] = true;
            assetToBathAsset[asset] = bathAsset;
        }
    }
}

File 7 of 11 : RubiconMarket.sol
/// SPDX-License-Identifier: Apache-2.0
/// This contract is a derivative work of the open-source work of Oasis DEX: https://github.com/OasisDEX/oasis

/// @title RubiconMarket.sol
/// @notice Please see the repository for this code at https://github.com/RubiconDeFi/rubicon_protocol;

pragma solidity =0.7.6;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/// @notice DSAuth events for authentication schema
contract DSAuthEvents {
    event LogSetAuthority(address indexed authority);
    event LogSetOwner(address indexed owner);
}

/// @notice DSAuth library for setting owner of the contract
/// @dev Provides the auth modifier for authenticated function calls
contract DSAuth is DSAuthEvents {
    address public owner;

    function setOwner(address owner_) external auth {
        owner = owner_;
        emit LogSetOwner(owner);
    }

    modifier auth {
        require(isAuthorized(msg.sender), "ds-auth-unauthorized");
        _;
    }

    function isAuthorized(address src) internal view returns (bool) {
        if (src == address(this)) {
            return true;
        } else if (src == owner) {
            return true;
        } else {
            return false;
        }
    }
}

/// @notice DSMath library for safe math without integer overflow/underflow
contract DSMath {
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, "ds-math-add-overflow");
    }

    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x - y) <= x, "ds-math-sub-underflow");
    }

    function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
    }

    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x <= y ? x : y;
    }

    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x >= y ? x : y;
    }

    function imin(int256 x, int256 y) internal pure returns (int256 z) {
        return x <= y ? x : y;
    }

    function imax(int256 x, int256 y) internal pure returns (int256 z) {
        return x >= y ? x : y;
    }

    uint256 constant WAD = 10**18;
    uint256 constant RAY = 10**27;

    function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), WAD / 2) / WAD;
    }

    function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), RAY / 2) / RAY;
    }

    function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, WAD), y / 2) / y;
    }

    function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, RAY), y / 2) / y;
    }
}

// /// @notice ERC-20 interface as derived from EIP-20
// contract ERC20 {
//     function totalSupply() public view returns (uint256);

//     function balanceOf(address guy) public view returns (uint256);

//     function allowance(address src, address guy) public view returns (uint256);

//     function approve(address guy, uint256 wad) public returns (bool);

//     function transfer(address dst, uint256 wad) public returns (bool);

//     function transferFrom(
//         address src,
//         address dst,
//         uint256 wad
//     ) public returns (bool);
// }

/// @notice Events contract for logging trade activity on Rubicon Market
/// @dev Provides the key event logs that are used in all core functionality of exchanging on the Rubicon Market
contract EventfulMarket {
    event LogItemUpdate(uint256 id);
    event LogTrade(
        uint256 pay_amt,
        address indexed pay_gem,
        uint256 buy_amt,
        address indexed buy_gem
    );

    event LogMake(
        bytes32 indexed id,
        bytes32 indexed pair,
        address indexed maker,
        ERC20 pay_gem,
        ERC20 buy_gem,
        uint128 pay_amt,
        uint128 buy_amt,
        uint64 timestamp
    );

    event LogBump(
        bytes32 indexed id,
        bytes32 indexed pair,
        address indexed maker,
        ERC20 pay_gem,
        ERC20 buy_gem,
        uint128 pay_amt,
        uint128 buy_amt,
        uint64 timestamp
    );

    event LogTake(
        bytes32 id,
        bytes32 indexed pair,
        address indexed maker,
        ERC20 pay_gem,
        ERC20 buy_gem,
        address indexed taker,
        uint128 take_amt,
        uint128 give_amt,
        uint64 timestamp
    );

    event LogKill(
        bytes32 indexed id,
        bytes32 indexed pair,
        address indexed maker,
        ERC20 pay_gem,
        ERC20 buy_gem,
        uint128 pay_amt,
        uint128 buy_amt,
        uint64 timestamp
    );

    event LogInt(string lol, uint256 input);

    event FeeTake(
        bytes32 id,
        bytes32 indexed pair,
        ERC20 asset,
        address indexed taker,
        address feeTo,
        uint256 feeAmt,
        uint64 timestamp
    );

    event OfferDeleted(uint256 id);
}

/// @notice Core trading logic for ERC-20 pairs, an orderbook, and transacting of tokens
/// @dev This contract holds the core ERC-20 / ERC-20 offer, buy, and cancel logic
contract SimpleMarket is EventfulMarket, DSMath {
    uint256 public last_offer_id;

    /// @dev The mapping that makes up the core orderbook of the exchange
    mapping(uint256 => OfferInfo) public offers;

    bool locked;

    /// @dev This parameter is in basis points
    uint256 internal feeBPS;

    /// @dev This parameter provides the address to which fees are sent
    address internal feeTo;

    struct OfferInfo {
        uint256 pay_amt;
        ERC20 pay_gem;
        uint256 buy_amt;
        ERC20 buy_gem;
        address owner;
        uint64 timestamp;
    }

    /// @notice Modifier that insures an order exists and is properly in the orderbook
    modifier can_buy(uint256 id) virtual {
        require(isActive(id));
        _;
    }

    /// @notice Modifier that checks the user to make sure they own the offer and its valid before they attempt to cancel it
    modifier can_cancel(uint256 id) virtual {
        require(isActive(id));
        require(getOwner(id) == msg.sender);
        _;
    }

    modifier can_offer virtual {
        _;
    }

    modifier synchronized {
        require(!locked);
        locked = true;
        _;
        locked = false;
    }

    function isActive(uint256 id) public view returns (bool active) {
        return offers[id].timestamp > 0;
    }

    function getOwner(uint256 id) public view returns (address owner) {
        return offers[id].owner;
    }

    function getOffer(uint256 id)
        public
        view
        returns (
            uint256,
            ERC20,
            uint256,
            ERC20
        )
    {
        OfferInfo memory _offer = offers[id];
        return (_offer.pay_amt, _offer.pay_gem, _offer.buy_amt, _offer.buy_gem);
    }

    /// @notice Below are the main public entrypoints

    function bump(bytes32 id_) external can_buy(uint256(id_)) {
        uint256 id = uint256(id_);
        emit LogBump(
            id_,
            keccak256(abi.encodePacked(offers[id].pay_gem, offers[id].buy_gem)),
            offers[id].owner,
            offers[id].pay_gem,
            offers[id].buy_gem,
            uint128(offers[id].pay_amt),
            uint128(offers[id].buy_amt),
            offers[id].timestamp
        );
    }

    /// @notice Accept a given `quantity` of an offer. Transfers funds from caller/taker to offer maker, and from market to caller/taker.
    /// @notice The fee for taker trades is paid in this function.
    function buy(uint256 id, uint256 quantity)
        public
        virtual
        can_buy(id)
        synchronized
        returns (bool)
    {
        OfferInfo memory _offer = offers[id];
        uint256 spend = mul(quantity, _offer.buy_amt) / _offer.pay_amt;

        require(uint128(spend) == spend, "spend is not an int");
        require(uint128(quantity) == quantity, "quantity is not an int");

        ///@dev For backwards semantic compatibility.
        if (
            quantity == 0 ||
            spend == 0 ||
            quantity > _offer.pay_amt ||
            spend > _offer.buy_amt
        ) {
            return false;
        }

        uint256 fee = mul(spend, feeBPS) / 10000;
        require(
            _offer.buy_gem.transferFrom(msg.sender, feeTo, fee),
            "Insufficient funds to cover fee"
        );

        offers[id].pay_amt = sub(_offer.pay_amt, quantity);
        offers[id].buy_amt = sub(_offer.buy_amt, spend);
        require(
            _offer.buy_gem.transferFrom(msg.sender, _offer.owner, spend),
            "_offer.buy_gem.transferFrom(msg.sender, _offer.owner, spend) failed - check that you can pay the fee"
        );
        require(
            _offer.pay_gem.transfer(msg.sender, quantity),
            "_offer.pay_gem.transfer(msg.sender, quantity) failed"
        );

        emit LogItemUpdate(id);
        emit LogTake(
            bytes32(id),
            keccak256(abi.encodePacked(_offer.pay_gem, _offer.buy_gem)),
            _offer.owner,
            _offer.pay_gem,
            _offer.buy_gem,
            msg.sender,
            uint128(quantity),
            uint128(spend),
            uint64(block.timestamp)
        );
        emit FeeTake(
            bytes32(id),
            keccak256(abi.encodePacked(_offer.pay_gem, _offer.buy_gem)),
            _offer.buy_gem,
            msg.sender,
            feeTo,
            fee,
            uint64(block.timestamp)
        );
        emit LogTrade(
            quantity,
            address(_offer.pay_gem),
            spend,
            address(_offer.buy_gem)
        );

        if (offers[id].pay_amt == 0) {
            delete offers[id];
            emit OfferDeleted(id);
        }

        return true;
    }

    /// @notice Allows the caller to cancel the offer if it is their own.
    /// @notice This function refunds the offer to the maker.
    function cancel(uint256 id)
        public
        virtual
        can_cancel(id)
        synchronized
        returns (bool success)
    {
        OfferInfo memory _offer = offers[id];
        delete offers[id];

        require(_offer.pay_gem.transfer(_offer.owner, _offer.pay_amt));

        emit LogItemUpdate(id);
        emit LogKill(
            bytes32(id),
            keccak256(abi.encodePacked(_offer.pay_gem, _offer.buy_gem)),
            _offer.owner,
            _offer.pay_gem,
            _offer.buy_gem,
            uint128(_offer.pay_amt),
            uint128(_offer.buy_amt),
            uint64(block.timestamp)
        );

        success = true;
    }

    function kill(bytes32 id) external virtual {
        require(cancel(uint256(id)));
    }

    function make(
        ERC20 pay_gem,
        ERC20 buy_gem,
        uint128 pay_amt,
        uint128 buy_amt
    ) external virtual returns (bytes32 id) {
        return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem));
    }

    /// @notice Key function to make a new offer. Takes funds from the caller into market escrow.
    function offer(
        uint256 pay_amt,
        ERC20 pay_gem,
        uint256 buy_amt,
        ERC20 buy_gem
    ) public virtual can_offer synchronized returns (uint256 id) {
        require(uint128(pay_amt) == pay_amt);
        require(uint128(buy_amt) == buy_amt);
        require(pay_amt > 0);
        require(pay_gem != ERC20(0x0));
        require(buy_amt > 0);
        require(buy_gem != ERC20(0x0));
        require(pay_gem != buy_gem);

        OfferInfo memory info;
        info.pay_amt = pay_amt;
        info.pay_gem = pay_gem;
        info.buy_amt = buy_amt;
        info.buy_gem = buy_gem;
        info.owner = msg.sender;
        info.timestamp = uint64(block.timestamp);
        id = _next_id();
        offers[id] = info;

        require(pay_gem.transferFrom(msg.sender, address(this), pay_amt));

        emit LogItemUpdate(id);
        emit LogMake(
            bytes32(id),
            keccak256(abi.encodePacked(pay_gem, buy_gem)),
            msg.sender,
            pay_gem,
            buy_gem,
            uint128(pay_amt),
            uint128(buy_amt),
            uint64(block.timestamp)
        );
    }

    function take(bytes32 id, uint128 maxTakeAmount) external virtual {
        require(buy(uint256(id), maxTakeAmount));
    }

    function _next_id() internal returns (uint256) {
        last_offer_id++;
        return last_offer_id;
    }

    // Fee logic
    function getFeeBPS() internal view returns (uint256) {
        return feeBPS;
    }
}

/// @notice Expiring market is a Simple Market with a market lifetime.
/// @dev When the close_time has been reached, offers can only be cancelled (offer and buy will throw).
contract ExpiringMarket is DSAuth, SimpleMarket {
    bool public stopped;

    /// @dev After close_time has been reached, no new offers are allowed.
    modifier can_offer override {
        require(!isClosed());
        _;
    }

    /// @dev After close, no new buys are allowed.
    modifier can_buy(uint256 id) override {
        require(isActive(id));
        require(!isClosed());
        _;
    }

    /// @dev After close, anyone can cancel an offer.
    modifier can_cancel(uint256 id) virtual override {
        require(isActive(id));
        require((msg.sender == getOwner(id)) || isClosed());
        _;
    }

    function isClosed() public pure returns (bool closed) {
        return false;
    }

    function getTime() public view returns (uint64) {
        return uint64(block.timestamp);
    }

    function stop() external auth {
        stopped = true;
    }
}

contract DSNote {
    event LogNote(
        bytes4 indexed sig,
        address indexed guy,
        bytes32 indexed foo,
        bytes32 indexed bar,
        uint256 wad,
        bytes fax
    ) anonymous;

    modifier note {
        bytes32 foo;
        bytes32 bar;
        uint256 wad;

        assembly {
            foo := calldataload(4)
            bar := calldataload(36)
            wad := callvalue()
        }

        emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);

        _;
    }
}

contract MatchingEvents {
    event LogBuyEnabled(bool isEnabled);
    event LogMinSell(address pay_gem, uint256 min_amount);
    event LogMatchingEnabled(bool isEnabled);
    event LogUnsortedOffer(uint256 id);
    event LogSortedOffer(uint256 id);
    event LogInsert(address keeper, uint256 id);
    event LogDelete(address keeper, uint256 id);
    event LogMatch(uint256 id, uint256 amount);
}

/// @notice The core Rubicon Market smart contract
/// @notice This contract is based on the original open-source work done by OasisDEX under the Apache License 2.0
/// @dev This contract inherits the key trading functionality from SimpleMarket
contract RubiconMarket is MatchingEvents, ExpiringMarket, DSNote {
    bool public buyEnabled = true; //buy enabled
    bool public matchingEnabled = true; //true: enable matching,
    //false: revert to expiring market
    /// @dev Below is variable to allow for a proxy-friendly constructor
    bool public initialized;

    bool public AqueductDistributionLive;
    address public AqueductAddress;

    struct sortInfo {
        uint256 next; //points to id of next higher offer
        uint256 prev; //points to id of previous lower offer
        uint256 delb; //the blocknumber where this entry was marked for delete
    }
    mapping(uint256 => sortInfo) public _rank; //doubly linked lists of sorted offer ids
    mapping(address => mapping(address => uint256)) public _best; //id of the highest offer for a token pair
    mapping(address => mapping(address => uint256)) public _span; //number of offers stored for token pair in sorted orderbook
    mapping(address => uint256) public _dust; //minimum sell amount for a token to avoid dust offers
    mapping(uint256 => uint256) public _near; //next unsorted offer id
    uint256 public _head; //first unsorted offer id
    uint256 public dustId; // id of the latest offer marked as dust

    /// @dev Proxy-safe initialization of storage
    function initialize(bool _live, address _feeTo) public {
        // require(msg.sender == ___deployer____);
        require(!initialized, "contract is already initialized");
        AqueductDistributionLive = _live;
        feeTo = _feeTo;

        owner = msg.sender;
        emit LogSetOwner(msg.sender);

        /// @notice The starting fee on taker trades in basis points
        feeBPS = 20;
        initialized = true;
        matchingEnabled = true;
        buyEnabled = true;
    }

    // After close, anyone can cancel an offer
    modifier can_cancel(uint256 id) override {
        require(isActive(id), "Offer was deleted or taken, or never existed.");
        require(
            isClosed() || msg.sender == getOwner(id) || id == dustId,
            "Offer can not be cancelled because user is not owner, and market is open, and offer sells required amount of tokens."
        );
        _;
    }

    // ---- Public entrypoints ---- //

    function make(
        ERC20 pay_gem,
        ERC20 buy_gem,
        uint128 pay_amt,
        uint128 buy_amt
    ) public override returns (bytes32) {
        return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem));
    }

    function take(bytes32 id, uint128 maxTakeAmount) public override {
        require(buy(uint256(id), maxTakeAmount));
    }

    function kill(bytes32 id) external override {
        require(cancel(uint256(id)));
    }

    // Make a new offer. Takes funds from the caller into market escrow.
    //
    // If matching is enabled:
    //     * creates new offer without putting it in
    //       the sorted list.
    //     * available to authorized contracts only!
    //     * keepers should call insert(id,pos)
    //       to put offer in the sorted list.
    //
    // If matching is disabled:
    //     * calls expiring market's offer().
    //     * available to everyone without authorization.
    //     * no sorting is done.
    //
    function offer(
        uint256 pay_amt, //maker (ask) sell how much
        ERC20 pay_gem, //maker (ask) sell which token
        uint256 buy_amt, //taker (ask) buy how much
        ERC20 buy_gem //taker (ask) buy which token
    ) public override returns (uint256) {
        require(!locked, "Reentrancy attempt");


            function(uint256, ERC20, uint256, ERC20) returns (uint256) fn
         = matchingEnabled ? _offeru : super.offer;
        return fn(pay_amt, pay_gem, buy_amt, buy_gem);
    }

    // Make a new offer. Takes funds from the caller into market escrow.
    function offer(
        uint256 pay_amt, //maker (ask) sell how much
        ERC20 pay_gem, //maker (ask) sell which token
        uint256 buy_amt, //maker (ask) buy how much
        ERC20 buy_gem, //maker (ask) buy which token
        uint256 pos //position to insert offer, 0 should be used if unknown
    ) external can_offer returns (uint256) {
        return offer(pay_amt, pay_gem, buy_amt, buy_gem, pos, true);
    }

    function offer(
        uint256 pay_amt, //maker (ask) sell how much
        ERC20 pay_gem, //maker (ask) sell which token
        uint256 buy_amt, //maker (ask) buy how much
        ERC20 buy_gem, //maker (ask) buy which token
        uint256 pos, //position to insert offer, 0 should be used if unknown
        bool matching //match "close enough" orders?
    ) public can_offer returns (uint256) {
        require(!locked, "Reentrancy attempt");
        require(_dust[address(pay_gem)] <= pay_amt);

        if (matchingEnabled) {
            return _matcho(pay_amt, pay_gem, buy_amt, buy_gem, pos, matching);
        }
        return super.offer(pay_amt, pay_gem, buy_amt, buy_gem);
    }

    //Transfers funds from caller to offer maker, and from market to caller.
    function buy(uint256 id, uint256 amount)
        public
        override
        can_buy(id)
        returns (bool)
    {
        require(!locked, "Reentrancy attempt");

        //Optional distribution on trade
        if (AqueductDistributionLive) {
            IAqueduct(AqueductAddress).distributeToMakerAndTaker(
                getOwner(id),
                msg.sender
            );
        }
        function(uint256, uint256) returns (bool) fn = matchingEnabled
            ? _buys
            : super.buy;

        return fn(id, amount);
    }

    // Cancel an offer. Refunds offer maker.
    function cancel(uint256 id)
        public
        override
        can_cancel(id)
        returns (bool success)
    {
        require(!locked, "Reentrancy attempt");
        if (matchingEnabled) {
            if (isOfferSorted(id)) {
                require(_unsort(id));
            } else {
                require(_hide(id));
            }
        }
        return super.cancel(id); //delete the offer.
    }

    //insert offer into the sorted list
    //keepers need to use this function
    function insert(
        uint256 id, //maker (ask) id
        uint256 pos //position to insert into
    ) public returns (bool) {
        require(!locked, "Reentrancy attempt");
        require(!isOfferSorted(id)); //make sure offers[id] is not yet sorted
        require(isActive(id)); //make sure offers[id] is active

        _hide(id); //remove offer from unsorted offers list
        _sort(id, pos); //put offer into the sorted offers list
        emit LogInsert(msg.sender, id);
        return true;
    }

    //deletes _rank [id]
    //  Function should be called by keepers.
    function del_rank(uint256 id) external returns (bool) {
        require(!locked, "Reentrancy attempt");
        require(
            !isActive(id) &&
                _rank[id].delb != 0 &&
                _rank[id].delb < block.number - 10
        );
        delete _rank[id];
        emit LogDelete(msg.sender, id);
        return true;
    }

    //set the minimum sell amount for a token
    //    Function is used to avoid "dust offers" that have
    //    very small amount of tokens to sell, and it would
    //    cost more gas to accept the offer, than the value
    //    of tokens received.
    function setMinSell(
        ERC20 pay_gem, //token to assign minimum sell amount to
        uint256 dust //maker (ask) minimum sell amount
    ) external auth note returns (bool) {
        _dust[address(pay_gem)] = dust;
        emit LogMinSell(address(pay_gem), dust);
        return true;
    }

    //returns the minimum sell amount for an offer
    function getMinSell(
        ERC20 pay_gem //token for which minimum sell amount is queried
    ) external view returns (uint256) {
        return _dust[address(pay_gem)];
    }

    //set buy functionality enabled/disabled
    function setBuyEnabled(bool buyEnabled_) external auth returns (bool) {
        buyEnabled = buyEnabled_;
        emit LogBuyEnabled(buyEnabled);
        return true;
    }

    //set matching enabled/disabled
    //    If matchingEnabled true(default), then inserted offers are matched.
    //    Except the ones inserted by contracts, because those end up
    //    in the unsorted list of offers, that must be later sorted by
    //    keepers using insert().
    //    If matchingEnabled is false then RubiconMarket is reverted to ExpiringMarket,
    //    and matching is not done, and sorted lists are disabled.
    function setMatchingEnabled(bool matchingEnabled_)
        external
        auth
        returns (bool)
    {
        matchingEnabled = matchingEnabled_;
        emit LogMatchingEnabled(matchingEnabled);
        return true;
    }

    //return the best offer for a token pair
    //      the best offer is the lowest one if it's an ask,
    //      and highest one if it's a bid offer
    function getBestOffer(ERC20 sell_gem, ERC20 buy_gem)
        public
        view
        returns (uint256)
    {
        return _best[address(sell_gem)][address(buy_gem)];
    }

    //return the next worse offer in the sorted list
    //      the worse offer is the higher one if its an ask,
    //      a lower one if its a bid offer,
    //      and in both cases the newer one if they're equal.
    function getWorseOffer(uint256 id) public view returns (uint256) {
        return _rank[id].prev;
    }

    //return the next better offer in the sorted list
    //      the better offer is in the lower priced one if its an ask,
    //      the next higher priced one if its a bid offer
    //      and in both cases the older one if they're equal.
    function getBetterOffer(uint256 id) external view returns (uint256) {
        return _rank[id].next;
    }

    //return the amount of better offers for a token pair
    function getOfferCount(ERC20 sell_gem, ERC20 buy_gem)
        public
        view
        returns (uint256)
    {
        return _span[address(sell_gem)][address(buy_gem)];
    }

    //get the first unsorted offer that was inserted by a contract
    //      Contracts can't calculate the insertion position of their offer because it is not an O(1) operation.
    //      Their offers get put in the unsorted list of offers.
    //      Keepers can calculate the insertion position offchain and pass it to the insert() function to insert
    //      the unsorted offer into the sorted list. Unsorted offers will not be matched, but can be bought with buy().
    function getFirstUnsortedOffer() public view returns (uint256) {
        return _head;
    }

    //get the next unsorted offer
    //      Can be used to cycle through all the unsorted offers.
    function getNextUnsortedOffer(uint256 id) public view returns (uint256) {
        return _near[id];
    }

    function isOfferSorted(uint256 id) public view returns (bool) {
        return
            _rank[id].next != 0 ||
            _rank[id].prev != 0 ||
            _best[address(offers[id].pay_gem)][address(offers[id].buy_gem)] ==
            id;
    }

    function sellAllAmount(
        ERC20 pay_gem,
        uint256 pay_amt,
        ERC20 buy_gem,
        uint256 min_fill_amount
    ) external returns (uint256 fill_amt) {
        require(!locked, "Reentrancy attempt");
        uint256 offerId;
        while (pay_amt > 0) {
            //while there is amount to sell
            offerId = getBestOffer(buy_gem, pay_gem); //Get the best offer for the token pair
            require(offerId != 0); //Fails if there are not more offers

            // There is a chance that pay_amt is smaller than 1 wei of the other token
            if (
                pay_amt * 1 ether <
                wdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)
            ) {
                break; //We consider that all amount is sold
            }
            if (pay_amt >= offers[offerId].buy_amt) {
                //If amount to sell is higher or equal than current offer amount to buy
                fill_amt = add(fill_amt, offers[offerId].pay_amt); //Add amount bought to acumulator
                pay_amt = sub(pay_amt, offers[offerId].buy_amt); //Decrease amount to sell
                take(bytes32(offerId), uint128(offers[offerId].pay_amt)); //We take the whole offer
            } else {
                // if lower
                uint256 baux = rmul(
                    pay_amt * 10**9,
                    rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)
                ) / 10**9;
                fill_amt = add(fill_amt, baux); //Add amount bought to acumulator
                take(bytes32(offerId), uint128(baux)); //We take the portion of the offer that we need
                pay_amt = 0; //All amount is sold
            }
        }
        require(fill_amt >= min_fill_amount);
    }

    function buyAllAmount(
        ERC20 buy_gem,
        uint256 buy_amt,
        ERC20 pay_gem,
        uint256 max_fill_amount
    ) external returns (uint256 fill_amt) {
        require(!locked, "Reentrancy attempt");
        uint256 offerId;
        while (buy_amt > 0) {
            //Meanwhile there is amount to buy
            offerId = getBestOffer(buy_gem, pay_gem); //Get the best offer for the token pair
            require(offerId != 0);

            // There is a chance that buy_amt is smaller than 1 wei of the other token
            if (
                buy_amt * 1 ether <
                wdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)
            ) {
                break; //We consider that all amount is sold
            }
            if (buy_amt >= offers[offerId].pay_amt) {
                //If amount to buy is higher or equal than current offer amount to sell
                fill_amt = add(fill_amt, offers[offerId].buy_amt); //Add amount sold to acumulator
                buy_amt = sub(buy_amt, offers[offerId].pay_amt); //Decrease amount to buy
                take(bytes32(offerId), uint128(offers[offerId].pay_amt)); //We take the whole offer
            } else {
                //if lower
                fill_amt = add(
                    fill_amt,
                    rmul(
                        buy_amt * 10**9,
                        rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)
                    ) / 10**9
                ); //Add amount sold to acumulator
                take(bytes32(offerId), uint128(buy_amt)); //We take the portion of the offer that we need
                buy_amt = 0; //All amount is bought
            }
        }
        require(fill_amt <= max_fill_amount);
    }

    function getBuyAmount(
        ERC20 buy_gem,
        ERC20 pay_gem,
        uint256 pay_amt
    ) external view returns (uint256 fill_amt) {
        uint256 offerId = getBestOffer(buy_gem, pay_gem); //Get best offer for the token pair
        while (pay_amt > offers[offerId].buy_amt) {
            fill_amt = add(fill_amt, offers[offerId].pay_amt); //Add amount to buy accumulator
            pay_amt = sub(pay_amt, offers[offerId].buy_amt); //Decrease amount to pay
            if (pay_amt > 0) {
                //If we still need more offers
                offerId = getWorseOffer(offerId); //We look for the next best offer
                require(offerId != 0); //Fails if there are not enough offers to complete
            }
        }
        fill_amt = add(
            fill_amt,
            rmul(
                pay_amt * 10**9,
                rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)
            ) / 10**9
        ); //Add proportional amount of last offer to buy accumulator
    }

    function getPayAmount(
        ERC20 pay_gem,
        ERC20 buy_gem,
        uint256 buy_amt
    ) external view returns (uint256 fill_amt) {
        uint256 offerId = getBestOffer(buy_gem, pay_gem); //Get best offer for the token pair
        while (buy_amt > offers[offerId].pay_amt) {
            fill_amt = add(fill_amt, offers[offerId].buy_amt); //Add amount to pay accumulator
            buy_amt = sub(buy_amt, offers[offerId].pay_amt); //Decrease amount to buy
            if (buy_amt > 0) {
                //If we still need more offers
                offerId = getWorseOffer(offerId); //We look for the next best offer
                require(offerId != 0); //Fails if there are not enough offers to complete
            }
        }
        fill_amt = add(
            fill_amt,
            rmul(
                buy_amt * 10**9,
                rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)
            ) / 10**9
        ); //Add proportional amount of last offer to pay accumulator
    }

    // ---- Internal Functions ---- //

    function _buys(uint256 id, uint256 amount) internal returns (bool) {
        require(buyEnabled);
        if (amount == offers[id].pay_amt) {
            if (isOfferSorted(id)) {
                //offers[id] must be removed from sorted list because all of it is bought
                _unsort(id);
            } else {
                _hide(id);
            }
        }

        require(super.buy(id, amount));

        // If offer has become dust during buy, we cancel it
        if (
            isActive(id) &&
            offers[id].pay_amt < _dust[address(offers[id].pay_gem)]
        ) {
            dustId = id; //enable current msg.sender to call cancel(id)
            cancel(id);
        }
        return true;
    }

    //find the id of the next higher offer after offers[id]
    function _find(uint256 id) internal view returns (uint256) {
        require(id > 0);

        address buy_gem = address(offers[id].buy_gem);
        address pay_gem = address(offers[id].pay_gem);
        uint256 top = _best[pay_gem][buy_gem];
        uint256 old_top = 0;

        // Find the larger-than-id order whose successor is less-than-id.
        while (top != 0 && _isPricedLtOrEq(id, top)) {
            old_top = top;
            top = _rank[top].prev;
        }
        return old_top;
    }

    //find the id of the next higher offer after offers[id]
    function _findpos(uint256 id, uint256 pos) internal view returns (uint256) {
        require(id > 0);

        // Look for an active order.
        while (pos != 0 && !isActive(pos)) {
            pos = _rank[pos].prev;
        }

        if (pos == 0) {
            //if we got to the end of list without a single active offer
            return _find(id);
        } else {
            // if we did find a nearby active offer
            // Walk the order book down from there...
            if (_isPricedLtOrEq(id, pos)) {
                uint256 old_pos;

                // Guaranteed to run at least once because of
                // the prior if statements.
                while (pos != 0 && _isPricedLtOrEq(id, pos)) {
                    old_pos = pos;
                    pos = _rank[pos].prev;
                }
                return old_pos;

                // ...or walk it up.
            } else {
                while (pos != 0 && !_isPricedLtOrEq(id, pos)) {
                    pos = _rank[pos].next;
                }
                return pos;
            }
        }
    }

    //return true if offers[low] priced less than or equal to offers[high]
    function _isPricedLtOrEq(
        uint256 low, //lower priced offer's id
        uint256 high //higher priced offer's id
    ) internal view returns (bool) {
        return
            mul(offers[low].buy_amt, offers[high].pay_amt) >=
            mul(offers[high].buy_amt, offers[low].pay_amt);
    }

    //these variables are global only because of solidity local variable limit

    //match offers with taker offer, and execute token transactions
    function _matcho(
        uint256 t_pay_amt, //taker sell how much
        ERC20 t_pay_gem, //taker sell which token
        uint256 t_buy_amt, //taker buy how much
        ERC20 t_buy_gem, //taker buy which token
        uint256 pos, //position id
        bool rounding //match "close enough" orders?
    ) internal returns (uint256 id) {
        uint256 best_maker_id; //highest maker id
        uint256 t_buy_amt_old; //taker buy how much saved
        uint256 m_buy_amt; //maker offer wants to buy this much token
        uint256 m_pay_amt; //maker offer wants to sell this much token

        // there is at least one offer stored for token pair
        while (_best[address(t_buy_gem)][address(t_pay_gem)] > 0) {
            best_maker_id = _best[address(t_buy_gem)][address(t_pay_gem)];
            m_buy_amt = offers[best_maker_id].buy_amt;
            m_pay_amt = offers[best_maker_id].pay_amt;

            // Ugly hack to work around rounding errors. Based on the idea that
            // the furthest the amounts can stray from their "true" values is 1.
            // Ergo the worst case has t_pay_amt and m_pay_amt at +1 away from
            // their "correct" values and m_buy_amt and t_buy_amt at -1.
            // Since (c - 1) * (d - 1) > (a + 1) * (b + 1) is equivalent to
            // c * d > a * b + a + b + c + d, we write...
            if (
                mul(m_buy_amt, t_buy_amt) >
                mul(t_pay_amt, m_pay_amt) +
                    (
                        rounding
                            ? m_buy_amt + t_buy_amt + t_pay_amt + m_pay_amt
                            : 0
                    )
            ) {
                break;
            }
            // ^ The `rounding` parameter is a compromise borne of a couple days
            // of discussion.
            buy(best_maker_id, min(m_pay_amt, t_buy_amt));
            emit LogMatch(id, min(m_pay_amt, t_buy_amt));
            t_buy_amt_old = t_buy_amt;
            t_buy_amt = sub(t_buy_amt, min(m_pay_amt, t_buy_amt));
            t_pay_amt = mul(t_buy_amt, t_pay_amt) / t_buy_amt_old;

            if (t_pay_amt == 0 || t_buy_amt == 0) {
                break;
            }
        }

        if (
            t_buy_amt > 0 &&
            t_pay_amt > 0 &&
            t_pay_amt >= _dust[address(t_pay_gem)]
        ) {
            //new offer should be created
            id = super.offer(t_pay_amt, t_pay_gem, t_buy_amt, t_buy_gem);
            //insert offer into the sorted list
            _sort(id, pos);
        }
    }

    // Make a new offer without putting it in the sorted list.
    // Takes funds from the caller into market escrow.
    // Keepers should call insert(id,pos) to put offer in the sorted list.
    function _offeru(
        uint256 pay_amt, //maker (ask) sell how much
        ERC20 pay_gem, //maker (ask) sell which token
        uint256 buy_amt, //maker (ask) buy how much
        ERC20 buy_gem //maker (ask) buy which token
    ) internal returns (uint256 id) {
        require(_dust[address(pay_gem)] <= pay_amt);
        id = super.offer(pay_amt, pay_gem, buy_amt, buy_gem);
        _near[id] = _head;
        _head = id;
        emit LogUnsortedOffer(id);
    }

    //put offer into the sorted list
    function _sort(
        uint256 id, //maker (ask) id
        uint256 pos //position to insert into
    ) internal {
        require(isActive(id));

        ERC20 buy_gem = offers[id].buy_gem;
        ERC20 pay_gem = offers[id].pay_gem;
        uint256 prev_id; //maker (ask) id

        pos = pos == 0 ||
            offers[pos].pay_gem != pay_gem ||
            offers[pos].buy_gem != buy_gem ||
            !isOfferSorted(pos)
            ? _find(id)
            : _findpos(id, pos);

        if (pos != 0) {
            //offers[id] is not the highest offer
            //requirement below is satisfied by statements above
            //require(_isPricedLtOrEq(id, pos));
            prev_id = _rank[pos].prev;
            _rank[pos].prev = id;
            _rank[id].next = pos;
        } else {
            //offers[id] is the highest offer
            prev_id = _best[address(pay_gem)][address(buy_gem)];
            _best[address(pay_gem)][address(buy_gem)] = id;
        }

        if (prev_id != 0) {
            //if lower offer does exist
            //requirement below is satisfied by statements above
            //require(!_isPricedLtOrEq(id, prev_id));
            _rank[prev_id].next = id;
            _rank[id].prev = prev_id;
        }

        _span[address(pay_gem)][address(buy_gem)]++;
        emit LogSortedOffer(id);
    }

    // Remove offer from the sorted list (does not cancel offer)
    function _unsort(
        uint256 id //id of maker (ask) offer to remove from sorted list
    ) internal returns (bool) {
        address buy_gem = address(offers[id].buy_gem);
        address pay_gem = address(offers[id].pay_gem);
        require(_span[pay_gem][buy_gem] > 0);

        require(
            _rank[id].delb == 0 && //assert id is in the sorted list
                isOfferSorted(id)
        );

        if (id != _best[pay_gem][buy_gem]) {
            // offers[id] is not the highest offer
            require(_rank[_rank[id].next].prev == id);
            _rank[_rank[id].next].prev = _rank[id].prev;
        } else {
            //offers[id] is the highest offer
            _best[pay_gem][buy_gem] = _rank[id].prev;
        }

        if (_rank[id].prev != 0) {
            //offers[id] is not the lowest offer
            require(_rank[_rank[id].prev].next == id);
            _rank[_rank[id].prev].next = _rank[id].next;
        }

        _span[pay_gem][buy_gem]--;
        _rank[id].delb = block.number; //mark _rank[id] for deletion
        return true;
    }

    //Hide offer from the unsorted order book (does not cancel offer)
    function _hide(
        uint256 id //id of maker offer to remove from unsorted list
    ) internal returns (bool) {
        uint256 uid = _head; //id of an offer in unsorted offers list
        uint256 pre = uid; //id of previous offer in unsorted offers list

        require(!isOfferSorted(id)); //make sure offer id is not in sorted offers list

        if (_head == id) {
            //check if offer is first offer in unsorted offers list
            _head = _near[id]; //set head to new first unsorted offer
            _near[id] = 0; //delete order from unsorted order list
            return true;
        }
        while (uid > 0 && uid != id) {
            //find offer in unsorted order list
            pre = uid;
            uid = _near[uid];
        }
        if (uid != id) {
            //did not find offer id in unsorted offers list
            return false;
        }
        _near[pre] = _near[id]; //set previous unsorted offer to point to offer after offer id
        _near[id] = 0; //delete order from unsorted order list
        return true;
    }

    function setFeeBPS(uint256 _newFeeBPS) external auth returns (bool) {
        feeBPS = _newFeeBPS;
        return true;
    }

    function setAqueductDistributionLive(bool live)
        external
        auth
        returns (bool)
    {
        AqueductDistributionLive = live;
        return true;
    }

    function setAqueductAddress(address _Aqueduct)
        external
        auth
        returns (bool)
    {
        AqueductAddress = _Aqueduct;
        return true;
    }

    function setFeeTo(address newFeeTo) external auth returns (bool) {
        feeTo = newFeeTo;
        return true;
    }
}

interface IWETH {
    function deposit() external payable;

    function transfer(address to, uint256 value) external returns (bool);

    function withdraw(uint256) external;

    function approve(address guy, uint256 wad) external returns (bool);
}

interface IAqueduct {
    function distributeToMakerAndTaker(address maker, address taker)
        external
        returns (bool);
}

File 8 of 11 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}

File 9 of 11 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 10 of 11 : USDCWithFaucet.sol
// SPDX-License-Identifier: UNLICENSED

// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
pragma solidity =0.7.6;

// import "./IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

contract LibNote {
    event LogNote(
        bytes4 indexed sig,
        address indexed usr,
        bytes32 indexed arg1,
        bytes32 indexed arg2,
        bytes data
    ) anonymous;
}

////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
// Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

/* pragma solidity 0.5.12; */

/* import "./lib.sol"; */

contract USDCWithFaucet is LibNote, ERC20 {
    // --- Auth ---
    mapping(address => uint256) public wards;
    address public admin;

    function rely(address guy) external auth {
        wards[guy] = 1;
    }

    function deny(address guy) external auth {
        wards[guy] = 0;
    }

    modifier auth {
        require(wards[msg.sender] == 1, "Dai/not-authorized");
        _;
    }

    // --- ERC20 Data ---
    string public constant version = "1";
    mapping(address => uint256) public faucetCheck;

    // --- Math ---
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x);
    }

    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x - y) <= x);
    }

    // --- EIP712 niceties ---
    bytes32 public DOMAIN_SEPARATOR;
    // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
    bytes32 public constant PERMIT_TYPEHASH =
        0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
    uint256 public timeDelay;

    constructor(
        uint256 chainId_,
        address _admin,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol) {
        wards[msg.sender] = 1;
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes(_name)),
                keccak256(bytes(version)),
                chainId_,
                address(this)
            )
        );
        admin = _admin;
        _mint(admin, 1000000e18);
        timeDelay = 8 days;
    }

    // --- Token ---
    function faucet() external returns (bool) {
        if (block.timestamp < faucetCheck[msg.sender] + timeDelay) {
            return false;
        }
        _mint(msg.sender, 1000e18);
        faucetCheck[msg.sender] = block.timestamp;
        return true;
    }

    function adminMint() external {
        require(admin == msg.sender);
        _mint(msg.sender, 10000e18);
    }

    function setTimeDelay(uint256 _timeDelay) external {
        require(admin == msg.sender);
        timeDelay = _timeDelay;
    }

    // --- Alias ---
    function push(address usr, uint256 wad) external {
        transferFrom(msg.sender, usr, wad);
    }

    function pull(address usr, uint256 wad) external {
        transferFrom(usr, msg.sender, wad);
    }

    function move(
        address src,
        address dst,
        uint256 wad
    ) external {
        transferFrom(src, dst, wad);
    }
}

File 11 of 11 : EquityToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.7.6;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// https://github.com/ethereum/eips/issues/1404
// contract ERC1404 is ERC20 {
//     function detectTransferRestriction (address from, address to, uint256 value) public view returns (uint8);
//     function messageForTransferRestriction (uint8 restrictionCode) public view returns (string memory);
// }

contract EquityToken is ERC20 {
    constructor(
        address admin,
        uint256 initialSupply,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol) {
        _mint(admin, initialSupply);
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"RubiconMarketAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategist","type":"address"}],"name":"approveStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedBathTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedPairs","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedStrategists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"doesAssetExist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"quote","type":"address"}],"name":"doesQuoteExist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"getBPSToStrats","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getBathPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getBathTokenfromAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"quote","type":"address"}],"name":"getBathTokenfromQuote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancelTimeDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarket","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"address","name":"pair","type":"address"},{"internalType":"uint8","name":"_propToStrategists","type":"uint8"}],"name":"initBathPair","outputs":[{"internalType":"address","name":"newPair","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"_reserveRatio","type":"uint256"},{"internalType":"uint256","name":"_timeDelay","type":"uint256"},{"internalType":"uint256","name":"mopc","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"isApprovedPair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wouldBeStrategist","type":"address"}],"name":"isApprovedStrategist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxOutstandingPairCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permissionedStrategists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"propToStrategists","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"removePair","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setBathHouseAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathPair","type":"address"},{"internalType":"uint16","name":"mosbps","type":"uint16"}],"name":"setBathPairMOSBPS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathPair","type":"address"},{"internalType":"int128","name":"val","type":"int128"}],"name":"setBathPairSCN","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathPair","type":"address"},{"internalType":"uint256","name":"sr","type":"uint256"}],"name":"setBathPairSearchRadius","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setBathTokenBathHouse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"uint256","name":"newBPS","type":"uint256"}],"name":"setBathTokenFeeBPS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"newMarket","type":"address"}],"name":"setBathTokenMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setCancelTimeDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bathToken","type":"address"},{"internalType":"address","name":"feeTo","type":"address"}],"name":"setFeeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMarket","type":"address"}],"name":"setMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setMaxOutstandingPairCount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_new","type":"bool"}],"name":"setPermissionedStrategists","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"value","type":"uint8"},{"internalType":"address","name":"pair","type":"address"}],"name":"setPropToStrats","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rr","type":"uint256"}],"name":"setReserveRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timeDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60806040523480156100195760008061001661001f565b50505b5061008a565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561005757808601518282016040015260200161003c565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b611ee4806100996000396000f3fe608060405234801561001957600080610016611ab9565b50505b50600436106101f75760003560e01c806301df192514610205578063023fa1d01461024857806306fdde03146102705780630c7d5cd8146102ef5780630f64afd014610309578063158ef93e1461033e578063191a4378146103465780631f3cdd8a1461037d57806320b46ff7146103c25780632f6bed9e146103f9578063319ddafb14610421578063382691df146104505780633de734841461047f5780634ec81af1146104875780635674def6146104c857806356b6d0d5146104f7578063599ff372146104ff5780635a7a5cbc146105645780636071015d1461058a57806360f71b24146105b9578063680cb3f8146105c157806368763e51146105f957806369b8dc9e146106285780636dcea85f1461065757806378851f7c146106865780637c047720146106b55780638adf5024146106e45780638b4f3b2c1461071c5780638f259e451461074257806393e2aea41461074a578063a8f096a414610346578063aa4eb2d21461077f578063ab511b9d146107ae578063af6c9c1d146107e7578063b3d7480e14610816578063c9dec3611461084d578063ca3a86cd14610855578063e43e270414610884578063e6a43905146108b3578063f1be1679146108ea578063f851a440146108f2578063fd58498e146108fa575b600080610202611ab9565b50505b6102346004803603602081101561022457600080610221611ab9565b50505b50356001600160a01b0316610902565b604051901515815260200160405180910390f35b61026e6004803603602081101561026757600080610264611ab9565b50505b5035610937565b005b610278610993565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156102b457808201518382015260200161029c565b50505050905090810190601f1680156102e15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102f7610a4c565b60405190815260200160405180910390f35b61026e6004803603604081101561032857600080610325611ab9565b50505b506001600160a01b038135169060200135610a59565b610234610b2e565b61026e6004803603604081101561036557600080610362611ab9565b50505b506001600160a01b0381358116916020013516610b47565b6103ac6004803603602081101561039c57600080610399611ab9565b50505b50356001600160a01b0316610bdd565b60405160ff909116815260200160405180910390f35b61026e600480360360408110156103e1576000806103de611ab9565b50505b506001600160a01b0381358116916020013516610c00565b61026e6004803603602081101561041857600080610415611ab9565b50505b50351515610c96565b610234600480360360208110156104405760008061043d611ab9565b50505b50356001600160a01b0316610d04565b6103ac6004803603602081101561046f5760008061046c611ab9565b50505b50356001600160a01b0316610d27565b610234610d41565b61026e600480360360808110156104a6576000806104a3611ab9565b50505b506001600160a01b038135169060208101359060408101359060600135610d4d565b61026e600480360360208110156104e7576000806104e4611ab9565b50505b50356001600160a01b0316610eff565b6102f7610f79565b6105486004803603608081101561051e5760008061051b611ab9565b50505b5080356001600160a01b03908116916020810135821691604082013516906060013560ff16610f8a565b6040516001600160a01b03909116815260200160405180910390f35b61026e6004803603602081101561058357600080610580611ab9565b50505b5035611299565b610234600480360360208110156105a9576000806105a6611ab9565b50505b50356001600160a01b03166112ef565b610548611356565b61026e600480360360408110156105e0576000806105dd611ab9565b50505b506001600160a01b038135169060200135600f0b611375565b61026e6004803603602081101561061857600080610615611ab9565b50505b50356001600160a01b0316611406565b6105486004803603602081101561064757600080610644611ab9565b50505b50356001600160a01b0316611476565b61026e6004803603602081101561067657600080610673611ab9565b50505b50356001600160a01b03166114b0565b610234600480360360208110156106a5576000806106a2611ab9565b50505b50356001600160a01b0316611508565b610548600480360360208110156106d4576000806106d1611ab9565b50505b50356001600160a01b0316611522565b61026e6004803603604081101561070357600080610700611ab9565b50505b50803560ff1690602001356001600160a01b0316611545565b61026e6004803603602081101561073b57600080610738611ab9565b50505b50356115ea565b6102f761166d565b61026e6004803603604081101561076957600080610766611ab9565b50505b506001600160a01b038135169060200135611677565b6102346004803603602081101561079e5760008061079b611ab9565b50505b50356001600160a01b0316611705565b61026e600480360360408110156107cd576000806107ca611ab9565b50505b5080356001600160a01b0316906020013561ffff1661171f565b61026e6004803603602081101561080657600080610803611ab9565b50505b50356001600160a01b03166117b0565b6105486004803603604081101561083557600080610832611ab9565b50505b506001600160a01b038135811691602001351661181d565b6102f7611872565b6102346004803603602081101561087457600080610871611ab9565b50505b50356001600160a01b031661187c565b610234600480360360208110156108a3576000806108a0611ab9565b50505b50356001600160a01b03166118bd565b610548600480360360408110156108d2576000806108cf611ab9565b50505b506001600160a01b03813581169160200135166118d7565b6105486118ff565b610548611921565b6102f761192d565b6001600160a01b0381166000908152600760205260408120600090610925611b24565b906101000a900460ff1690505b919050565b60006002610943611b24565b906101000a90046001600160a01b03166001600160a01b03165a610965611b84565b6001600160a01b0316146109815760008061097e611ab9565b50505b8080600e61098d611bca565b50505050565b60008061099e611b24565b600181600116156101000203166002900480601f0160208091040260200160405190810160405281815291906020830182806109d8611b24565b60018160011615610100020316600290048015610a445780601f10610a12576101008083610a04611b24565b040283529160200191610a44565b820191906000526020600020905b81610a29611b24565b81529060010190602001808311610a2057829003601f168201915b505050505081565b600d610a56611b24565b81565b60006002610a65611b24565b906101000a90046001600160a01b03166001600160a01b03165a610a87611b84565b6001600160a01b031614610aa357600080610aa0611ab9565b50505b816001600160a01b031663604b6a9c826040516001600160e01b031960e084901b168152600481019190915260240160006040518083038160008780610ae7611c18565b158015610afc57600080610af9611ab9565b50505b505a610b06611c64565b505050505050158015610b26573d6000803e3d6000610b23611ab9565b50505b505050505050565b6000600c610b3a611b24565b906101000a900460ff1681565b60006002610b53611b24565b906101000a90046001600160a01b03166001600160a01b03165a610b75611b84565b6001600160a01b031614610b9157600080610b8e611ab9565b50505b816001600160a01b0316636dcea85f826040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160006040518083038160008780610ae7611c18565b6001600160a01b0381166000908152600960205260408120600090610925611b24565b60006002610c0c611b24565b906101000a90046001600160a01b03166001600160a01b03165a610c2e611b84565b6001600160a01b031614610c4a57600080610c47611ab9565b50505b816001600160a01b031663f46901ed826040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160006040518083038160008780610ae7611c18565b60006002610ca2611b24565b906101000a90046001600160a01b03166001600160a01b03165a610cc4611b84565b6001600160a01b031614610ce057600080610cdd611ab9565b50505b80600c61010081610cef611b24565b8160ff0219169083151502179061098d611bca565b6001600160a01b0381166000908152600860205260408120600090610925611b24565b600960205280600052604060002060009150610b3a611b24565b6001600c610b3a611b24565b6000600c610d59611b24565b906101000a900460ff1615610d7657600080610d73611ab9565b50505b60405160408082019052601281527152756269636f6e204261746820486f75736560701b60208201526000908051610db2929160200190611d4f565b505a610dbc611b84565b6002600181610dc9611b24565b816001600160a01b0302191690836001600160a01b0316021790610deb611bca565b50505081600e8190610dfb611bca565b5050506064831115610e1557600080610e12611ab9565b50505b60008311610e2b57600080610e28611ab9565b50505b8280600d610e37611bca565b50505080600f8190610e47611bca565b50505083600360006101000a81610e5c611b24565b816001600160a01b0302191690836001600160a01b0316021790610e7e611bca565b505050610ea66002600090610e91611b24565b906101000a90046001600160a01b0316611406565b6001600c61010081610eb6611b24565b8160ff02191690831515021790610ecb611bca565b5050506001600c60006101000a81610ee1611b24565b8160ff02191690831515021790610ef6611bca565b50505050505050565b60006002610f0b611b24565b906101000a90046001600160a01b03166001600160a01b03165a610f2d611b84565b6001600160a01b031614610f4957600080610f46611ab9565b50505b806002600181610f57611b24565b816001600160a01b0302191690836001600160a01b031602179061098d611bca565b6000600d610f85611b24565b905090565b6000806002610f97611b24565b906101000a90046001600160a01b03166001600160a01b03165a610fb9611b84565b6001600160a01b031614610fd557600080610fd2611ab9565b50505b836001600160a01b0316856001600160a01b03161415610ffd57600080610ffa611ab9565b50505b6001600160a01b03851661101957600080611016611ab9565b50505b6001600160a01b03841661103557600080611032611ab9565b50505b6001600160a01b0385166000908152600160205261108a90604090206001600160a01b038616600090815260209190915260409020600090611075611b24565b906101000a90046001600160a01b031661187c565b1561109d5760008061109a611ab9565b50505b6001600160a01b038316600090815260096020528290604090206001816110c2611b24565b8160ff021916908360ff160217906110d8611bca565b5050506001600160a01b038516600090815260016020528390604090206001600160a01b038616600090815260209190915260409020600181611119611b24565b816001600160a01b0302191690836001600160a01b031602179061113b611bca565b50505061114783611939565b6111ec84846001600160a01b031663e22a78076040518163ffffffff1660e01b8152600401602060405180830381868061117f611c18565b15801561119457600080611191611ab9565b50505b505a61119e611dff565b50505050501580156111bd573d6000803e3d60006111ba611ab9565b50505b505050506040513d60208110156111dc576000806111d9611ab9565b50505b810190808051925061195f915050565b61129085846001600160a01b0316623acabc6040518163ffffffff1660e01b81526004016020604051808303818680611223611c18565b15801561123857600080611235611ab9565b50505b505a611242611dff565b5050505050158015611261573d6000803e3d600061125e611ab9565b50505b505050506040513d60208110156112805760008061127d611ab9565b50505b8101908080519250611a1f915050565b50909392505050565b600060026112a5611b24565b906101000a90046001600160a01b03166001600160a01b03165a6112c7611b84565b6001600160a01b0316146112e3576000806112e0611ab9565b50505b8080600f61098d611bca565b6001600160a01b0381166000908152600660205260408120600090611312611b24565b906101000a900460ff16151560011515148061134157506001600c611335611b24565b906101000a900460ff16155b1561134e57506001610932565b506000610932565b60006003611362611b24565b906101000a90046001600160a01b031681565b60006002611381611b24565b906101000a90046001600160a01b03166001600160a01b03165a6113a3611b84565b6001600160a01b0316146113bf576000806113bc611ab9565b50505b816001600160a01b03166318447307826040516001600160e01b031960e084901b168152600f9190910b600482015260240160006040518083038160008780610ae7611c18565b60006002611412611b24565b906101000a90046001600160a01b03166001600160a01b03165a611434611b84565b6001600160a01b0316146114505760008061144d611ab9565b50505b6001600160a01b0381166000908152600660205260019060409020600181610cef611b24565b6001600160a01b0381166000908152600a60205260408120600090611499611b24565b906101000a90046001600160a01b03169050919050565b600060026114bc611b24565b906101000a90046001600160a01b03166001600160a01b03165a6114de611b84565b6001600160a01b0316146114fa576000806114f7611ab9565b50505b806003600181610f57611b24565b600660205280600052604060002060009150610b3a611b24565b6001600160a01b0381166000908152600b60205260408120600090611499611b24565b60006002611551611b24565b906101000a90046001600160a01b03166001600160a01b03165a611573611b84565b6001600160a01b03161461158f5760008061158c611ab9565b50505b60328260ff16106115a8576000806115a5611ab9565b50505b6001600160a01b038116600090815260096020528290604090206001816115cd611b24565b8160ff021916908360ff160217906115e3611bca565b5050505050565b600060026115f6611b24565b906101000a90046001600160a01b03166001600160a01b03165a611618611b84565b6001600160a01b03161461163457600080611631611ab9565b50505b606481111561164b57600080611648611ab9565b50505b600081116116615760008061165e611ab9565b50505b8080600d61098d611bca565b600f610a56611b24565b60006002611683611b24565b906101000a90046001600160a01b03166001600160a01b03165a6116a5611b84565b6001600160a01b0316146116c1576000806116be611ab9565b50505b816001600160a01b03166314a933b2826040516001600160e01b031960e084901b168152600481019190915260240160006040518083038160008780610ae7611c18565b600560205280600052604060002060009150610b3a611b24565b6000600261172b611b24565b906101000a90046001600160a01b03166001600160a01b03165a61174d611b84565b6001600160a01b03161461176957600080611766611ab9565b50505b816001600160a01b031663028d01fe826040516001600160e01b031960e084901b16815261ffff909116600482015260240160006040518083038160008780610ae7611c18565b600060026117bc611b24565b906101000a90046001600160a01b03166001600160a01b03165a6117de611b84565b6001600160a01b0316146117fa576000806117f7611ab9565b50505b6001600160a01b0381166000908152600560205260408120600181610cef611b24565b6001600160a01b03821660009081526001602052604081206001600160a01b038316600090815260209190915260409020600090611859611b24565b906101000a90046001600160a01b031690505b92915050565b600e610a56611b24565b6001600160a01b038116600090815260056020526040812060009061189f611b24565b906101000a900460ff16151560011515141561134e57506001610932565b600460205280600052604060002060009150610b3a611b24565b6001602052816000526040600020602052806000526040600020600092509050611362611b24565b600080600361190c611b24565b906101000a90046001600160a01b0316905090565b60006002611362611b24565b6000600e610f85611b24565b6001600160a01b0381166000908152600560205260019060409020600181610cef611b24565b6001600160a01b0382166000908152600760205260409020600090611982611b24565b906101000a900460ff161561199657611a1b565b6001600160a01b03821660009081526007602052600190604090206001816119bc611b24565b8160ff021916908315150217906119d1611bca565b5050506001600160a01b0382166000908152600a6020528190604090206001816119f9611b24565b816001600160a01b0302191690836001600160a01b03160217906115e3611bca565b5050565b6001600160a01b0382166000908152600860205260409020600090611a42611b24565b906101000a900460ff1615611a5657611a1b565b6001600160a01b0382166000908152600860205260019060409020600181611a7c611b24565b8160ff02191690831515021790611a91611bca565b5050506001600160a01b0382166000908152600b6020528190604090206001816119f9611b24565b632a2a7adb598160e01b8152600481016020815285602082015260005b86811015611af1578086015182820160400152602001611ad6565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015611b7f57600082820152602001611b68565b505050565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b80516000825293506020611b68565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b600081526020611b68565b638435035b598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b80516000825293506020611b68565b6385979f76598160e01b8152611c95565b808083111561186c575090919050565b808083101561186c575090919050565b836004820152846024820152606060448201528760648201526084810160005b89811015611ccd578089015182820152602001611cb5565b506060828a60a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8c8c82606087013350600060045af15059611d228e3d611c85565b8d01611d2e8187611c75565b5b82811015611d435760008152602001611d2f565b50929d50505050505050565b8280611d59611b24565b600181600116156101000203166002900490600052602060002090601f016020900481019282611d9557600085611d8e611bca565b5050611def565b82601f10611db057805160ff19168380011785611d8e611bca565b82800160010185611dbf611bca565b50508215611def579182015b82811115611def57825182611dde611bca565b505091602001919060010190611dcb565b50611dfb929150611ec5565b5090565b638540661f598160e01b8152836004820152846024820152606060448201528660648201526084810160005b88811015611e43578088015182820152602001611e2b565b506060828960a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8b8b82606087013350600060045af15059611e988d3d611c85565b8c01611ea48187611c75565b5b82811015611eb95760008152602001611ea5565b50929c50505050505050565b80821115611dfb5760008082611ed9611bca565b505050600101611ec556

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101ee5760003560e01c806301df1925146101f3578063023fa1d01461022d57806306fdde031461024c5780630c7d5cd8146102c95780630f64afd0146102e3578063158ef93e1461030f578063191a4378146103175780631f3cdd8a1461034557806320b46ff7146103815780632f6bed9e146103af578063319ddafb146103ce578063382691df146103f45780633de734841461041a5780634ec81af1146104225780635674def61461045a57806356b6d0d514610480578063599ff372146104885780635a7a5cbc146104e45780636071015d1461050157806360f71b2414610527578063680cb3f81461052f57806368763e511461055e57806369b8dc9e146105845780636dcea85f146105aa57806378851f7c146105d05780637c047720146105f65780638adf50241461061c5780638b4f3b2c1461064b5780638f259e451461066857806393e2aea414610670578063a8f096a414610317578063aa4eb2d21461069c578063ab511b9d146106c2578063af6c9c1d146106f2578063b3d7480e14610718578063c9dec36114610746578063ca3a86cd1461074e578063e43e270414610774578063e6a439051461079a578063f1be1679146107c8578063f851a440146107d0578063fd58498e146107d8575b600080fd5b6102196004803603602081101561020957600080fd5b50356001600160a01b03166107e0565b604080519115158252519081900360200190f35b61024a6004803603602081101561024357600080fd5b5035610802565b005b61025461081e565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561028e578181015183820152602001610276565b50505050905090810190601f1680156102bb5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102d16108ac565b60408051918252519081900360200190f35b61024a600480360360408110156102f957600080fd5b506001600160a01b0381351690602001356108b2565b61021961092b565b61024a6004803603604081101561032d57600080fd5b506001600160a01b0381358116916020013516610934565b61036b6004803603602081101561035b57600080fd5b50356001600160a01b031661099a565b6040805160ff9092168252519081900360200190f35b61024a6004803603604081101561039757600080fd5b506001600160a01b03813581169160200135166109b8565b61024a600480360360208110156103c557600080fd5b50351515610a1e565b610219600480360360208110156103e457600080fd5b50356001600160a01b0316610a4f565b61036b6004803603602081101561040a57600080fd5b50356001600160a01b0316610a6d565b610219610a82565b61024a6004803603608081101561043857600080fd5b506001600160a01b038135169060208101359060408101359060600135610a90565b61024a6004803603602081101561047057600080fd5b50356001600160a01b0316610b61565b6102d1610b9a565b6104c86004803603608081101561049e57600080fd5b5080356001600160a01b03908116916020810135821691604082013516906060013560ff16610ba0565b604080516001600160a01b039092168252519081900360200190f35b61024a600480360360208110156104fa57600080fd5b5035610d79565b6102196004803603602081101561051757600080fd5b50356001600160a01b0316610d95565b6104c8610dde565b61024a6004803603604081101561054557600080fd5b506001600160a01b038135169060200135600f0b610ded565b61024a6004803603602081101561057457600080fd5b50356001600160a01b0316610e4d565b6104c86004803603602081101561059a57600080fd5b50356001600160a01b0316610e88565b61024a600480360360208110156105c057600080fd5b50356001600160a01b0316610ea6565b610219600480360360208110156105e657600080fd5b50356001600160a01b0316610edf565b6104c86004803603602081101561060c57600080fd5b50356001600160a01b0316610ef4565b61024a6004803603604081101561063257600080fd5b50803560ff1690602001356001600160a01b0316610f12565b61024a6004803603602081101561066157600080fd5b5035610f64565b6102d1610f9b565b61024a6004803603604081101561068657600080fd5b506001600160a01b038135169060200135610fa1565b610219600480360360208110156106b257600080fd5b50356001600160a01b0316610ffe565b61024a600480360360408110156106d857600080fd5b5080356001600160a01b0316906020013561ffff16611013565b61024a6004803603602081101561070857600080fd5b50356001600160a01b0316611074565b6104c86004803603604081101561072e57600080fd5b506001600160a01b03813581169160200135166110ac565b6102d16110d6565b6102196004803603602081101561076457600080fd5b50356001600160a01b03166110dc565b6102196004803603602081101561078a57600080fd5b50356001600160a01b031661110a565b6104c8600480360360408110156107b057600080fd5b506001600160a01b038135811691602001351661111f565b6104c8611145565b6104c8611154565b6102d1611163565b6001600160a01b03811660009081526007602052604090205460ff165b919050565b6002546001600160a01b0316331461081957600080fd5b600e55565b6000805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156108a45780601f10610879576101008083540402835291602001916108a4565b820191906000526020600020905b81548152906001019060200180831161088757829003601f168201915b505050505081565b600d5481565b6002546001600160a01b031633146108c957600080fd5b816001600160a01b031663604b6a9c826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561090f57600080fd5b505af1158015610923573d6000803e3d6000fd5b505050505050565b600c5460ff1681565b6002546001600160a01b0316331461094b57600080fd5b816001600160a01b0316636dcea85f826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561090f57600080fd5b6001600160a01b031660009081526009602052604090205460ff1690565b6002546001600160a01b031633146109cf57600080fd5b816001600160a01b031663f46901ed826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561090f57600080fd5b6002546001600160a01b03163314610a3557600080fd5b600c80549115156101000261ff0019909216919091179055565b6001600160a01b031660009081526008602052604090205460ff1690565b60096020526000908152604090205460ff1681565b600c54610100900460ff1681565b600c5460ff1615610aa057600080fd5b6040805180820190915260128082527152756269636f6e204261746820486f75736560701b6020909201918252610ad991600091611268565b50600280546001600160a01b03191633179055600e8290556064831115610aff57600080fd5b60008311610b0c57600080fd5b600d839055600f819055600380546001600160a01b0319166001600160a01b0386811691909117909155600254610b439116610e4d565b5050600c805460ff1961ff0019909116610100171660011790555050565b6002546001600160a01b03163314610b7857600080fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b600d5490565b6002546000906001600160a01b03163314610bba57600080fd5b836001600160a01b0316856001600160a01b03161415610bd957600080fd5b6001600160a01b038516610bec57600080fd5b6001600160a01b038416610bff57600080fd5b6001600160a01b0380861660009081526001602090815260408083208885168452909152902054610c3091166110dc565b15610c3a57600080fd5b6001600160a01b038381166000818152600960209081526040808320805460ff191660ff891617905589851683526001825280832094891683529390529190912080546001600160a01b0319169091179055610c9583611169565b610d0384846001600160a01b031663e22a78076040518163ffffffff1660e01b815260040160206040518083038186803b158015610cd257600080fd5b505afa158015610ce6573d6000803e3d6000fd5b505050506040513d6020811015610cfc57600080fd5b505161118d565b610d7085846001600160a01b0316623acabc6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d3f57600080fd5b505afa158015610d53573d6000803e3d6000fd5b505050506040513d6020811015610d6957600080fd5b50516111fd565b50909392505050565b6002546001600160a01b03163314610d9057600080fd5b600f55565b6001600160a01b03811660009081526006602052604081205460ff16151560011480610dc95750600c54610100900460ff16155b15610dd6575060016107fd565b5060006107fd565b6003546001600160a01b031681565b6002546001600160a01b03163314610e0457600080fd5b816001600160a01b03166318447307826040518263ffffffff1660e01b81526004018082600f0b8152602001915050600060405180830381600087803b15801561090f57600080fd5b6002546001600160a01b03163314610e6457600080fd5b6001600160a01b03166000908152600660205260409020805460ff19166001179055565b6001600160a01b039081166000908152600a60205260409020541690565b6002546001600160a01b03163314610ebd57600080fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b60066020526000908152604090205460ff1681565b6001600160a01b039081166000908152600b60205260409020541690565b6002546001600160a01b03163314610f2957600080fd5b60328260ff1610610f3957600080fd5b6001600160a01b03166000908152600960205260409020805460ff191660ff92909216919091179055565b6002546001600160a01b03163314610f7b57600080fd5b6064811115610f8957600080fd5b60008111610f9657600080fd5b600d55565b600f5481565b6002546001600160a01b03163314610fb857600080fd5b816001600160a01b03166314a933b2826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561090f57600080fd5b60056020526000908152604090205460ff1681565b6002546001600160a01b0316331461102a57600080fd5b816001600160a01b031663028d01fe826040518263ffffffff1660e01b8152600401808261ffff168152602001915050600060405180830381600087803b15801561090f57600080fd5b6002546001600160a01b0316331461108b57600080fd5b6001600160a01b03166000908152600560205260409020805460ff19169055565b6001600160a01b039182166000908152600160209081526040808320938516835292905220541690565b600e5481565b6001600160a01b03811660009081526005602052604081205460ff16151560011415610dd6575060016107fd565b60046020526000908152604090205460ff1681565b60016020908152600092835260408084209091529082529020546001600160a01b031681565b6003546001600160a01b031690565b6002546001600160a01b031681565b600e5490565b6001600160a01b03166000908152600560205260409020805460ff19166001179055565b6001600160a01b03821660009081526007602052604090205460ff16156111b3576111f9565b6001600160a01b038083166000908152600760209081526040808320805460ff19166001179055600a909152902080549183166001600160a01b03199092169190911790555b5050565b6001600160a01b03821660009081526008602052604090205460ff1615611223576111f9565b6001600160a01b0391821660009081526008602090815260408083208054600160ff19909116179055600b909152902080546001600160a01b03191691909216179055565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261129e57600085556112e4565b82601f106112b757805160ff19168380011785556112e4565b828001600101855582156112e4579182015b828111156112e45782518255916020019190600101906112c9565b506112f09291506112f4565b5090565b5b808211156112f057600081556001016112f556fea26469706673582212205220bbaa82ac2504c2c9fa5b207941bed2e1992b3e26286d3f72f9e1f7020da864736f6c63430007060033

Deployed ByteCode Sourcemap

280:7252:8:-:0;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;5455:114;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;5455:114:8;-1:-1:-1;;;;;5455:114:8;;:::i;:::-;;;;;;;;;;;;;;;;;3177:96;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;3177:96:8;;:::i;:::-;;305:18;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1112:27;;;:::i;:::-;;;;;;;;;;;;;;;4074:157;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;4074:157:8;;;;;;;;:::i;952:23::-;;;:::i;3735:163::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;3735:163:8;;;;;;;;;;:::i;6013:115::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;6013:115:8;-1:-1:-1;;;;;6013:115:8;;:::i;:::-;;;;;;;;;;;;;;;;;;4237:124;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;4237:124:8;;;;;;;;;;:::i;3008:113::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;3008:113:8;;;;:::i;5575:114::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;5575:114:8;-1:-1:-1;;;;;5575:114:8;;:::i;777:50::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;777:50:8;-1:-1:-1;;;;;777:50:8;;:::i;981:35::-;;;:::i;1485:571::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;1485:571:8;;;;;;;;;;;;;;;;;;:::i;2905:97::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;2905:97:8;-1:-1:-1;;;;;2905:97:8;;:::i;5086:95::-;;;:::i;2062:756::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;2062:756:8;;-1:-1:-1;;;;;2062:756:8;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;2062:756:8;;;;;;;;;;;;;;3611:118;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;3611:118:8;;:::i;6185:323::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;6185:323:8;-1:-1:-1;;;;;6185:323:8;;:::i;425:35::-;;;:::i;4697:129::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;4697:129:8;;;;;;;;;;:::i;6713:119::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;6713:119:8;-1:-1:-1;;;;;6713:119:8;;:::i;5695:153::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;5695:153:8;-1:-1:-1;;;;;5695:153:8;;:::i;4832:106::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;4832:106:8;-1:-1:-1;;;;;4832:106:8;;:::i;609:51::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;609:51:8;-1:-1:-1;;;;;609:51:8;;:::i;5854:153::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;5854:153:8;-1:-1:-1;;;;;5854:153:8;;:::i;3428:177::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;3428:177:8;;;;;;;;-1:-1:-1;;;;;3428:177:8;;:::i;3279:143::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;3279:143:8;;:::i;1390:38::-;;;:::i;4534:157::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;4534:157:8;;;;;;;;:::i;558:45::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;558:45:8;-1:-1:-1;;;;;558:45:8;;:::i;4367:161::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;4367:161:8;;-1:-1:-1;;;;;4367:161:8;;;;;;;;:::i;6931:97::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;6931:97:8;-1:-1:-1;;;;;6931:97:8;;:::i;5288:161::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;5288:161:8;;;;;;;;;;:::i;1268:24::-;;;:::i;6514:193::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;6514:193:8;-1:-1:-1;;;;;6514:193:8;;:::i;502:50::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;502:50:8;-1:-1:-1;;;;;502:50:8;;:::i;330:62::-;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;330:62:8;;;;;;;;;;:::i;4983:97::-;;;:::i;399:20::-;;;:::i;5187:95::-;;;:::i;5455:114::-;-1:-1:-1;;;;;5540:22:8;;5517:4;5540:22;;;:15;:22;;;5517:4;5540:22;;;;;:::i;:::-;;;;;;;;5533:29;;5455:114;;;;:::o;3177:96::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;3261:5;;3249:9:::1;:17;;:::i;:::-;;;;3177:96:::0;:::o;305:18::-;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1112:27::-;;;;:::i;:::-;;:::o;4074:157::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;4196:9:::1;-1:-1:-1::0;;;;;4186:30:8::1;;4217:6;4186:38;;-1:-1:-1::0;;;;;;4186:38:8::1;::::0;;;;;;::::1;::::0;::::1;::::0;;;;;;-1:-1:-1;4186:38:8::1;;;;;;;;;;;:::i;:::-;;;;;;;::::0;::::1;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;::::0;::::1;;;;;:::i;:::-;;;;;;;;4074:157:::0;;:::o;952:23::-;;;;;:::i;:::-;;;;;;;;;:::o;3735:163::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;3860:9:::1;-1:-1:-1::0;;;;;3850:30:8::1;;3881:9;3850:41;;-1:-1:-1::0;;;;;;3850:41:8::1;::::0;;;;;;-1:-1:-1;;;;;3850:41:8;;::::1;;::::0;::::1;::::0;;;-1:-1:-1;3850:41:8::1;;;;;;;;;;;:::i;6013:115::-:0;-1:-1:-1;;;;;6098:23:8;;6074:5;6098:23;;;:17;:23;;;6074:5;6098:23;;;;;:::i;4237:124::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;4328:9:::1;-1:-1:-1::0;;;;;4318:29:8::1;;4348:5;4318:36;;-1:-1:-1::0;;;;;;4318:36:8::1;::::0;;;;;;-1:-1:-1;;;;;4318:36:8;;::::1;;::::0;::::1;::::0;;;-1:-1:-1;4318:36:8::1;;;;;;;;;;;:::i;3008:113::-:0;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;3110:4;3084:23:::1;:30;:23:::0;:30:::1;;:::i;:::-;;;;;;;;;;;;;;;:::i;5575:114::-:0;-1:-1:-1;;;;;5660:22:8;;5637:4;5660:22;;;:15;:22;;;5637:4;5660:22;;;;;:::i;777:50::-;;;;;;;;;;;;;;;:::i;981:35::-;;;;;:::i;1485:571::-;1644:11;;;;:::i;:::-;;;;;;;;1643:12;1635:21;;;;;;:::i;:::-;;;;1666:27;;;;;;;;;;;-1:-1:-1;;;1666:27:8;;;;-1:-1:-1;;1666:27:8;;;;;;;;;:::i;:::-;;1711:10;;;:::i;:::-;1703:5;:18;:5;:18;;:::i;:::-;;-1:-1:-1;;;;;1703:18:8;;;;;-1:-1:-1;;;;;1703:18:8;;;;;;:::i;:::-;;;;1743:10;1731:9;:22;;;;:::i;:::-;;;;1788:3;1771:13;:20;;1763:29;;;;;;:::i;:::-;;;;1826:1;1810:13;:17;1802:26;;;;;;:::i;:::-;;;;1853:13;;1838:12;:28;;:::i;:::-;;;;1903:4;1877:23;:30;;;;:::i;:::-;;;;1941:6;1918:20;;:29;;;;;:::i;:::-;;-1:-1:-1;;;;;1918:29:8;;;;;-1:-1:-1;;;;;1918:29:8;;;;;;:::i;:::-;;;;1957:24;1975:5;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;1975:5:8;1957:17;:24::i;:::-;2017:4;1991:23;:30;:23;:30;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;2045:4;2031:11;;:18;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;1485:571;;;;:::o;2905:97::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;2987:8;2979:5:::1;:16:::0;:5;:16:::1;;:::i;:::-;;-1:-1:-1::0;;;;;2979:16:8::1;;;;;-1:-1:-1::0;;;;;2979:16:8::1;;;;;;:::i;5086:95::-:0;5136:7;5162:12;;;:::i;:::-;5155:19;;5086:95;:::o;2062:756::-;2220:15;;2875:5;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;2328:5:::1;-1:-1:-1::0;;;;;2319:14:8::1;:5;-1:-1:-1::0;;;;;2319:14:8::1;;;2311:23;;;::::0;::::1;;:::i;:::-;;;;-1:-1:-1::0;;;;;2352:19:8;::::1;2344:28;;;::::0;::::1;;:::i;:::-;;;;-1:-1:-1::0;;;;;2390:19:8;::::1;2382:28;;;::::0;::::1;;:::i;:::-;;;;-1:-1:-1::0;;;;;2499:14:8;::::1;;::::0;;;:7:::1;:14;::::0;2484:37:::1;::::0;2499:14;;::::1;-1:-1:-1::0;;;;;2499:21:8;::::1;;::::0;;;::::1;::::0;;;;;;::::1;;;;;:::i;:::-;;;;;;-1:-1:-1::0;;;;;2499:21:8::1;2484:14;:37::i;:::-;2483:38;2475:47;;;::::0;::::1;;:::i;:::-;;;;-1:-1:-1::0;;;;;2532:23:8;::::1;;::::0;;;:17:::1;:23;::::0;2558:18;;2532:23;;::::1;:44:::0;;::::1;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;;;;2587:14:8;::::1;;::::0;;;:7:::1;:14;::::0;2619:4;;2587:14;;::::1;-1:-1:-1::0;;;;;2587:21:8;::::1;;::::0;;;::::1;::::0;;;;;;::::1;:37:::0;;::::1;;:::i;:::-;;-1:-1:-1::0;;;;;2587:37:8::1;;;;;-1:-1:-1::0;;;;;2587:37:8::1;;;;;;:::i;:::-;;;;2635:26;2655:4;2635:11;:26::i;:::-;2671:50;2680:5;2696:4;-1:-1:-1::0;;;;;2687:31:8::1;;:33;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;::::0;::::1;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;::::0;::::1;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;:::i;:::-;;;;;;;;;;::::0;-1:-1:-1;2671:8:8::1;::::0;-1:-1:-1;;2671:50:8:i:1;:::-;2731;2740:5;2756:4;-1:-1:-1::0;;;;;2747:31:8::1;;:33;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;::::0;::::1;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;::::0;::::1;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;:::i;:::-;;;;;;;;;;::::0;-1:-1:-1;2731:8:8::1;::::0;-1:-1:-1;;2731:50:8:i:1;:::-;-1:-1:-1::0;2806:4:8;;2062:756;-1:-1:-1;;;2062:756:8:o;3611:118::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;3717:5;;3691:23:::1;:31;;:::i;6185:323::-:0;-1:-1:-1;;;;;6326:38:8;;6289:4;6326:38;;;:19;:38;;;6289:4;6326:38;;;;;:::i;:::-;;;;;;;;:46;;6368:4;6326:46;;;:86;;;-1:-1:-1;6389:23:8;;;;:::i;:::-;;;;;;;;6388:24;6326:86;6309:193;;;-1:-1:-1;6444:4:8;6437:11;;6309:193;-1:-1:-1;6486:5:8;6479:12;;425:35;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;425:35:8;;:::o;4697:129::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;4789:8:::1;-1:-1:-1::0;;;;;4780:34:8::1;;4815:3;4780:39;;-1:-1:-1::0;;;;;;4780:39:8::1;::::0;;;;;;::::1;::::0;;;::::1;;::::0;::::1;::::0;;;-1:-1:-1;4780:39:8::1;;;;;;;;;;;:::i;6713:119::-:0;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;6787:31:8;::::1;;::::0;;;:19:::1;:31;::::0;6821:4:::1;::::0;6787:31;;::::1;:38:::0;;::::1;;:::i;5695:153::-:0;-1:-1:-1;;;;;5818:23:8;;5788:7;5818:23;;;:16;:23;;;5788:7;5818:23;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;5818:23:8;5811:30;;5695:153;;;:::o;4832:106::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;4922:9;4899:20:::1;:32:::0;:20;:32:::1;;:::i;609:51::-:0;;;;;;;;;;;;;;;:::i;5854:153::-;-1:-1:-1;;;;;5977:23:8;;5947:7;5977:23;;;:16;:23;;;5947:7;5977:23;;;;;:::i;3428:177::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;3525:2:::1;3517:5;:10;;;3509:19;;;::::0;::::1;;:::i;:::-;;;;-1:-1:-1::0;;;;;3567:23:8;::::1;;::::0;;;:17:::1;:23;::::0;3593:5;;3567:23;;::::1;:31:::0;;::::1;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;3428:177:::0;;:::o;3279:143::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;3359:3:::1;3353:2;:9;;3345:18;;;::::0;::::1;;:::i;:::-;;;;3386:1;3381:2;:6;3373:15;;;::::0;::::1;;:::i;:::-;;;;3413:2:::0;;3398:12:::1;:17;;:::i;1390:38::-:0;;;;:::i;4534:157::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;4655:8:::1;-1:-1:-1::0;;;;;4646:34:8::1;;4681:2;4646:38;;-1:-1:-1::0;;;;;;4646:38:8::1;::::0;;;;;;::::1;::::0;::::1;::::0;;;;;;-1:-1:-1;4646:38:8::1;;;;;;;;;;;:::i;558:45::-:0;;;;;;;;;;;;;;;:::i;4367:161::-;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;4485:8:::1;-1:-1:-1::0;;;;;4476:37:8::1;;4514:6;4476:45;;-1:-1:-1::0;;;;;;4476:45:8::1;::::0;;;;;;::::1;::::0;;::::1;;::::0;::::1;::::0;;;-1:-1:-1;4476:45:8::1;;;;;;;;;;;:::i;6931:97::-:0;2875:5;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;2875:5:8;-1:-1:-1;;;;;2861:19:8;:10;;;:::i;:::-;-1:-1:-1;;;;;2861:19:8;;2853:28;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;6994:19:8;::::1;7016:5;6994:19:::0;;;:13:::1;:19;::::0;;7016:5;6994:19:::1;:27:::0;;::::1;;:::i;5288:161::-:0;-1:-1:-1;;;;;5421:14:8;;5386:12;5421:14;;;:7;:14;;;5386:12;5421:14;-1:-1:-1;;;;;5421:21:8;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;5421:21:8;5414:28;;5288:161;;;;;:::o;1268:24::-;;;;:::i;6514:193::-;-1:-1:-1;;;;;6593:19:8;;6573:4;6593:19;;;:13;:19;;;6573:4;6593:19;;;;;:::i;:::-;;;;;;;;:27;;6616:4;6593:27;;;6589:112;;;-1:-1:-1;6643:4:8;6636:11;;502:50;;;;;;;;;;;;;;;:::i;330:62::-;;;;;;;;;;;;;;;;;;;;-1:-1:-1;330:62:8;-1:-1:-1;330:62:8;;:::i;4983:97::-;5027:7;;5053:20;;;:::i;:::-;;;;;;-1:-1:-1;;;;;5053:20:8;5046:27;;4983:97;:::o;399:20::-;;;;;:::i;5187:95::-;5240:7;5266:9;;;:::i;6838:87::-;-1:-1:-1;;;;;6892:19:8;;;;;;:13;:19;;6914:4;;6892:19;;;:26;;;;:::i;7034:245::-;-1:-1:-1;;;;;7109:22:8;;;;;;:15;:22;;;;;;;;;:::i;:::-;;;;;;;;7105:168;;;7147:7;;7105:168;-1:-1:-1;;;;;7184:22:8;;;;;;:15;:22;;7209:4;;7184:22;;;:29;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;;;;7227:23:8;;;;;;:16;:23;;7253:9;;7227:23;;;:35;;;;:::i;:::-;;-1:-1:-1;;;;;7227:35:8;;;;;-1:-1:-1;;;;;7227:35:8;;;;;;:::i;7105:168::-;7034:245;;:::o;7285:::-;-1:-1:-1;;;;;7360:22:8;;;;;;:15;:22;;;;;;;;;:::i;:::-;;;;;;;;7356:168;;;7398:7;;7356:168;-1:-1:-1;;;;;7435:22:8;;;;;;:15;:22;;7460:4;;7435:22;;;:29;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;;;;7478:23:8;;;;;;:16;:23;;7504:9;;7478:23;;;:35;;;;:::i;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;

Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.