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_f7adf6a1df921b1aed77ca4bb17c5ce78c29c773 | 0x60806040 | 0 | 1169 days 13 hrs ago | GENESIS | IN | Create: BathHouse | 0 ETH | 0 |
[ Download CSV Export ]
Contract Source Code Verified (Genesis Bytecode Match Only)
Contract Name:
BathHouse
Compiler Version
v0.7.6
Contract Source Code (Solidity Standard Json-Input format)
// 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); } } }
// 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; } } }
// 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); }
// 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; } }
// 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); } }
// 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; } } }
/// 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); }
// 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 { } }
// 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; } }
// 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); } }
// 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); } }
{ "optimizer": { "enabled": true, "runs": 1 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "metadata": { "useLiteralContent": true } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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;:::-;;;;;;;
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.