Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- FortOptions
- Optimization enabled
- true
- Compiler version
- v0.8.13+commit.abaa5c0e
- Optimization runs
- 888888
- EVM Version
- default
- Verified at
- 2022-03-21T09:55:30.964770Z
Contract source code
// Sources flattened with hardhat v2.5.0 https://hardhat.org
// File contracts/libs/ABDKMath64x64.sol
// SPDX-License-Identifier: BSD-4-Clause
/*
* ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting.
* Author: Mikhail Vladimirov <[email protected]>
*/
pragma solidity ^0.8.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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
require (x <= 0x7FFFFFFFFFFFFFFF);
return int128 (int256 (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) {
unchecked {
require (x >= 0);
return uint64 (uint128 (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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
if (y == 0) return 0;
require (x >= 0);
uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
uint256 hi = uint256 (int256 (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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
bool negative = x < 0 && y & 1 == 1;
uint256 absX = uint128 (x < 0 ? -x : x);
uint256 absResult;
absResult = 0x100000000000000000000000000000000;
if (absX <= 0x10000000000000000) {
absX <<= 63;
while (y != 0) {
if (y & 0x1 != 0) {
absResult = absResult * absX >> 127;
}
absX = absX * absX >> 127;
if (y & 0x2 != 0) {
absResult = absResult * absX >> 127;
}
absX = absX * absX >> 127;
if (y & 0x4 != 0) {
absResult = absResult * absX >> 127;
}
absX = absX * absX >> 127;
if (y & 0x8 != 0) {
absResult = absResult * absX >> 127;
}
absX = absX * absX >> 127;
y >>= 4;
}
absResult >>= 64;
} else {
uint256 absXShift = 63;
if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }
if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }
if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }
if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }
if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }
if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }
uint256 resultShift = 0;
while (y != 0) {
require (absXShift < 64);
if (y & 0x1 != 0) {
absResult = absResult * absX >> 127;
resultShift += absXShift;
if (absResult > 0x100000000000000000000000000000000) {
absResult >>= 1;
resultShift += 1;
}
}
absX = absX * absX >> 127;
absXShift <<= 1;
if (absX >= 0x100000000000000000000000000000000) {
absX >>= 1;
absXShift += 1;
}
y >>= 1;
}
require (resultShift < 64);
absResult >>= 64 - resultShift;
}
int256 result = negative ? -int256 (absResult) : int256 (absResult);
require (result >= MIN_64x64 && result <= MAX_64x64);
return int128 (result);
}
}
/**
* 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) {
unchecked {
require (x >= 0);
return int128 (sqrtu (uint256 (int256 (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) {
unchecked {
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 (int256 (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) {
unchecked {
require (x > 0);
return int128 (int256 (
uint256 (int256 (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) {
unchecked {
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 (int256 (63 - (x >> 64)));
require (result <= uint256 (int256 (MAX_64x64)));
return int128 (int256 (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) {
unchecked {
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) {
unchecked {
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 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) {
unchecked {
if (x == 0) return 0;
else {
uint256 xx = x;
uint256 r = 1;
if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }
if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }
if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }
if (xx >= 0x10000) { xx >>= 16; r <<= 8; }
if (xx >= 0x100) { xx >>= 8; r <<= 4; }
if (xx >= 0x10) { xx >>= 4; r <<= 2; }
if (xx >= 0x8) { r <<= 1; }
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1; // Seven iterations should be enough
uint256 r1 = x / r;
return uint128 (r < r1 ? r : r1);
}
}
}
}
// File contracts/interfaces/IFortOptions.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev Define methods for european option
interface IFortOptions {
/// @dev Option structure for view methods
struct OptionView {
uint index;
address tokenAddress;
uint strikePrice;
bool orientation;
uint exerciseBlock;
uint balance;
address owner;
}
/// @dev Option open event
/// @param index Index of option
/// @param dcuAmount Amount of paid DCU
/// @param owner Owner of this option
/// @param amount Amount of option
event Open(
uint index,
uint dcuAmount,
address owner,
uint amount
);
/// @dev Option exercise event
/// @param index Index of option
/// @param amount Amount of option to exercise
/// @param owner Owner of this option
/// @param gain Amount of dcu gained
event Exercise(uint index, uint amount, address owner, uint gain);
/// @dev Option sell event
/// @param index Index of option
/// @param amount Amount of option to sell
/// @param owner Owner of this option
/// @param dcuAmount Amount of dcu acquired
event Sell(uint index, uint amount, address owner, uint dcuAmount);
/// @dev Returns the share of the specified option for target address
/// @param index Index of the option
/// @param addr Target address
function balanceOf(uint index, address addr) external view returns (uint);
/// @dev Find the options of the target address (in reverse order)
/// @param start Find forward from the index corresponding to the given contract address
/// (excluding the record corresponding to start)
/// @param count Maximum number of records returned
/// @param maxFindCount Find records at most
/// @param owner Target address
/// @return optionArray Matched option array
function find(
uint start,
uint count,
uint maxFindCount,
address owner
) external view returns (OptionView[] memory optionArray);
/// @dev List options
/// @param offset Skip previous (offset) records
/// @param count Return (count) records
/// @param order Order. 0 reverse order, non-0 positive order
/// @return optionArray Matched option array
function list(uint offset, uint count, uint order) external view returns (OptionView[] memory optionArray);
/// @dev Obtain the number of European options that have been opened
/// @return Number of European options opened
function getOptionCount() external view returns (uint);
/// @dev Estimate the amount of option
/// @param tokenAddress Target token address, 0 means eth
/// @param oraclePrice Current price from oracle
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @param dcuAmount Amount of paid DCU
/// @return amount Amount of option
function estimate(
address tokenAddress,
uint oraclePrice,
uint strikePrice,
bool orientation,
uint exerciseBlock,
uint dcuAmount
) external view returns (uint amount);
/// @dev Open option
/// @param tokenAddress Target token address, 0 means eth
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @param dcuAmount Amount of paid DCU
function open(
address tokenAddress,
uint strikePrice,
bool orientation,
uint exerciseBlock,
uint dcuAmount
) external payable;
/// @dev Exercise option
/// @param index Index of option
/// @param amount Amount of option to exercise
function exercise(uint index, uint amount) external payable;
/// @dev Sell option
/// @param index Index of option
/// @param amount Amount of option to sell
function sell(uint index, uint amount) external payable;
/// @dev Calculate option price
/// @param oraclePrice Current price from oracle
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @return v Option price. Need to divide (USDT_BASE << 64)
function calcV(
address tokenAddress,
uint oraclePrice,
uint strikePrice,
bool orientation,
uint exerciseBlock
) external view returns (uint v);
}
// File contracts/custom/ChainParameter.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev Base contract of Fort
contract ChainParameter {
// Block time. ethereum 14 seconds, BSC 3 seconds, polygon 2.2 seconds
uint constant BLOCK_TIME = 3;
// Minimal exercise block period. 840000
// TODO:
uint constant MIN_PERIOD = 10;
}
// File contracts/interfaces/IFortMapping.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev The interface defines methods for Fort builtin contract address mapping
interface IFortMapping {
/// @dev Address updated event
/// @param name Address name
/// @param oldAddress Old address
/// @param newAddress New address
event AddressUpdated(string name, address oldAddress, address newAddress);
/// @dev Set the built-in contract address of the system
/// @param dcuToken Address of dcu token contract
/// @param fortDAO IFortDAO implementation contract address
/// @param fortOptions IFortOptions implementation contract address
/// @param fortFutures IFortFutures implementation contract address
/// @param fortVaultForStaking IFortVaultForStaking implementation contract address
/// @param nestPriceFacade INestPriceFacade implementation contract address
function setBuiltinAddress(
address dcuToken,
address fortDAO,
address fortOptions,
address fortFutures,
address fortVaultForStaking,
address nestPriceFacade
) external;
/// @dev Get the built-in contract address of the system
/// @return dcuToken Address of dcu token contract
/// @return fortDAO IFortDAO implementation contract address
/// @return fortOptions IFortOptions implementation contract address
/// @return fortFutures IFortFutures implementation contract address
/// @return fortVaultForStaking IFortVaultForStaking implementation contract address
/// @return nestPriceFacade INestPriceFacade implementation contract address
function getBuiltinAddress() external view returns (
address dcuToken,
address fortDAO,
address fortOptions,
address fortFutures,
address fortVaultForStaking,
address nestPriceFacade
);
/// @dev Get address of dcu token contract
/// @return Address of dcu token contract
function getDCUTokenAddress() external view returns (address);
/// @dev Get IFortDAO implementation contract address
/// @return IFortDAO implementation contract address
function getFortDAOAddress() external view returns (address);
/// @dev Get IFortOptions implementation contract address
/// @return IFortOptions implementation contract address
function getFortOptionsAddress() external view returns (address);
/// @dev Get IFortFutures implementation contract address
/// @return IFortFutures implementation contract address
function getFortFuturesAddress() external view returns (address);
/// @dev Get IFortVaultForStaking implementation contract address
/// @return IFortVaultForStaking implementation contract address
function getFortVaultForStakingAddress() external view returns (address);
/// @dev Get INestPriceFacade implementation contract address
/// @return INestPriceFacade implementation contract address
function getNestPriceFacade() external view returns (address);
/// @dev Registered address. The address registered here is the address accepted by Fort system
/// @param key The key
/// @param addr Destination address. 0 means to delete the registration information
function registerAddress(string calldata key, address addr) external;
/// @dev Get registered address
/// @param key The key
/// @return Destination address. 0 means empty
function checkAddress(string calldata key) external view returns (address);
}
// File contracts/interfaces/IFortGovernance.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev This interface defines the governance methods
interface IFortGovernance is IFortMapping {
/// @dev Governance flag changed event
/// @param addr Target address
/// @param oldFlag Old governance flag
/// @param newFlag New governance flag
event FlagChanged(address addr, uint oldFlag, uint newFlag);
/// @dev Set governance authority
/// @param addr Destination address
/// @param flag Weight. 0 means to delete the governance permission of the target address. Weight is not
/// implemented in the current system, only the difference between authorized and unauthorized.
/// Here, a uint96 is used to represent the weight, which is only reserved for expansion
function setGovernance(address addr, uint flag) external;
/// @dev Get governance rights
/// @param addr Destination address
/// @return Weight. 0 means to delete the governance permission of the target address. Weight is not
/// implemented in the current system, only the difference between authorized and unauthorized.
/// Here, a uint96 is used to represent the weight, which is only reserved for expansion
function getGovernance(address addr) external view returns (uint);
/// @dev Check whether the target address has governance rights for the given target
/// @param addr Destination address
/// @param flag Permission weight. The permission of the target address must be greater than this weight
/// to pass the check
/// @return True indicates permission
function checkGovernance(address addr, uint flag) external view returns (bool);
}
// File contracts/FortBase.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev Base contract of Fort
contract FortBase {
/// @dev Governance address changed event
/// @param oldGovernance Old governance address
/// @param newGovernance New governance address
event GovernanceChanged(address oldGovernance, address newGovernance);
/// @dev IFortGovernance implementation contract address
address public _governance;
/// @dev To support open-zeppelin/upgrades
/// @param governance IFortGovernance implementation contract address
function initialize(address governance) public virtual {
require(_governance == address(0), "Fort:!initialize");
emit GovernanceChanged(address(0), governance);
_governance = governance;
}
/// @dev Rewritten in the implementation contract, for load other contract addresses. Call
/// super.update(newGovernance) when overriding, and override method without onlyGovernance
/// @param newGovernance IFortGovernance implementation contract address
function update(address newGovernance) public virtual {
address governance = _governance;
require(governance == msg.sender || IFortGovernance(governance).checkGovernance(msg.sender, 0), "Fort:!gov");
emit GovernanceChanged(governance, newGovernance);
_governance = newGovernance;
}
//---------modifier------------
modifier onlyGovernance() {
require(IFortGovernance(_governance).checkGovernance(msg.sender, 0), "Fort:!gov");
_;
}
}
// File contracts/custom/FortFrequentlyUsed.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
// /// @dev Base contract of Fort
// contract FortFrequentlyUsed is FortBase {
// // Address of DCU contract
// address constant DCU_TOKEN_ADDRESS = 0xf56c6eCE0C0d6Fbb9A53282C0DF71dBFaFA933eF;
// // Address of NestOpenPrice contract
// address constant NEST_OPEN_PRICE = 0x09CE0e021195BA2c1CDE62A8B187abf810951540;
// // USDT base
// uint constant USDT_BASE = 1 ether;
// }
// TODO:
/// @dev Base contract of Fort
contract FortFrequentlyUsed is FortBase {
// Address of DCU contract
//address constant DCU_TOKEN_ADDRESS = ;
address DCU_TOKEN_ADDRESS;
// Address of NestPriceFacade contract
//address constant NEST_OPEN_PRICE = 0xB5D2890c061c321A5B6A4a4254bb1522425BAF0A;
address NEST_OPEN_PRICE;
// USDT base
uint constant USDT_BASE = 1 ether;
/// @dev Rewritten in the implementation contract, for load other contract addresses. Call
/// super.update(newGovernance) when overriding, and override method without onlyGovernance
/// @param newGovernance IFortGovernance implementation contract address
function update(address newGovernance) public override {
super.update(newGovernance);
(
DCU_TOKEN_ADDRESS,//address dcuToken,
,//address fortDAO,
,//address fortOptions,
,//address fortFutures,
,//address fortVaultForStaking,
NEST_OPEN_PRICE //address nestPriceFacade
) = IFortGovernance(newGovernance).getBuiltinAddress();
}
}
// File contracts/interfaces/INestBatchPrice2.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev This contract implemented the mining logic of nest
interface INestBatchPrice2 {
/// @dev Get the latest trigger price
/// @param channelId Target channelId
/// @param pairIndices Array of pair indices
/// @param payback Address to receive refund
/// @return prices Price array, i * 2 is the block where the ith price is located, and i * 2 + 1 is the ith price
function triggeredPrice(
uint channelId,
uint[] calldata pairIndices,
address payback
) external payable returns (uint[] memory prices);
/// @dev Get the full information of latest trigger price
/// @param channelId Target channelId
/// @param pairIndices Array of pair indices
/// @param payback Address to receive refund
/// @return prices Price array, i * 4 is the block where the ith price is located, i * 4 + 1 is the ith price,
/// i * 4 + 2 is the ith average price and i * 4 + 3 is the ith volatility
function triggeredPriceInfo(
uint channelId,
uint[] calldata pairIndices,
address payback
) external payable returns (uint[] memory prices);
/// @dev Find the price at block number
/// @param channelId Target channelId
/// @param pairIndices Array of pair indices
/// @param height Destination block number
/// @param payback Address to receive refund
/// @return prices Price array, i * 2 is the block where the ith price is located, and i * 2 + 1 is the ith price
function findPrice(
uint channelId,
uint[] calldata pairIndices,
uint height,
address payback
) external payable returns (uint[] memory prices);
/// @dev Get the last (num) effective price
/// @param channelId Target channelId
/// @param pairIndices Array of pair indices
/// @param count The number of prices that want to return
/// @param payback Address to receive refund
/// @return prices Result array, i * count * 2 to (i + 1) * count * 2 - 1 are
/// the price results of group i quotation pairs
function lastPriceList(
uint channelId,
uint[] calldata pairIndices,
uint count,
address payback
) external payable returns (uint[] memory prices);
/// @dev Returns lastPriceList and triggered price info
/// @param channelId Target channelId
/// @param pairIndices Array of pair indices
/// @param count The number of prices that want to return
/// @param payback Address to receive refund
/// @return prices result of group i quotation pair. Among them, the first two count * are the latest prices,
/// and the last four are: trigger price block number, trigger price, average price and volatility
function lastPriceListAndTriggeredPriceInfo(
uint channelId,
uint[] calldata pairIndices,
uint count,
address payback
) external payable returns (uint[] memory prices);
}
// File contracts/custom/FortPriceAdapter.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev Base contract of Fort
contract FortPriceAdapter is FortFrequentlyUsed {
// token configuration
struct TokenConfig {
// The channelId for call nest price
uint16 channelId;
// The pairIndex for call nest price
uint16 pairIndex;
// SigmaSQ for token
uint64 sigmaSQ;
// MIU_LONG for token
uint64 miuLong;
// MIU_SHORT for token
uint64 miuShort;
}
// Post unit: 2000usd
uint constant POST_UNIT = 2000 * USDT_BASE;
function _pairIndices(uint pairIndex) private pure returns (uint[] memory pairIndices) {
pairIndices = new uint[](1);
pairIndices[0] = pairIndex;
}
// Query latest 2 price
function _lastPriceList(
TokenConfig memory tokenConfig,
uint fee,
address payback
) internal returns (uint[] memory prices) {
prices = INestBatchPrice2(NEST_OPEN_PRICE).lastPriceList {
value: fee
} (uint(tokenConfig.channelId), _pairIndices(uint(tokenConfig.pairIndex)), 2, payback);
prices[1] = _toUSDTPrice(prices[1]);
prices[3] = _toUSDTPrice(prices[3]);
}
// Query latest price
function _latestPrice(
TokenConfig memory tokenConfig,
uint fee,
address payback
) internal returns (uint oraclePrice) {
uint[] memory prices = INestBatchPrice2(NEST_OPEN_PRICE).lastPriceList {
value: fee
} (uint(tokenConfig.channelId), _pairIndices(uint(tokenConfig.pairIndex)), 1, payback);
oraclePrice = _toUSDTPrice(prices[1]);
}
// Find price by blockNumber
function _findPrice(
TokenConfig memory tokenConfig,
uint blockNumber,
uint fee,
address payback
) internal returns (uint oraclePrice) {
uint[] memory prices = INestBatchPrice2(NEST_OPEN_PRICE).findPrice {
value: fee
} (uint(tokenConfig.channelId), _pairIndices(uint(tokenConfig.pairIndex)), blockNumber, payback);
oraclePrice = _toUSDTPrice(prices[1]);
}
// Convert to usdt based price
function _toUSDTPrice(uint rawPrice) internal pure returns (uint) {
return POST_UNIT * 1 ether / rawPrice;
}
}
// File @openzeppelin/contracts/token/ERC20/[email protected]
// MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
// MIT
pragma solidity ^0.8.0;
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// File @openzeppelin/contracts/utils/[email protected]
// MIT
pragma solidity ^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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// File @openzeppelin/contracts/token/ERC20/[email protected]
// MIT
pragma solidity ^0.8.0;
/**
* @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, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override 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 this function is
* overridden;
*
* 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 override returns (uint8) {
return 18;
}
/**
* @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);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
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] + 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) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This 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);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(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:
*
* - `account` 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 += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(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);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(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 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 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 {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// File contracts/DCU.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev DCU token
contract DCU is FortBase, ERC20("Decentralized Currency Unit", "DCU") {
/// @dev Mining permission flag change event
/// @param account Target address
/// @param oldFlag Old flag
/// @param newFlag New flag
event MinterChanged(address account, uint oldFlag, uint newFlag);
// Flags for account
mapping(address=>uint) _flags;
constructor() {
}
/// @dev Set mining permission flag
/// @param account Target address
/// @param flag Mining permission flag
function setMinter(address account, uint flag) external onlyGovernance {
emit MinterChanged(account, _flags[account], flag);
_flags[account] = flag;
}
/// @dev Check mining permission flag
/// @param account Target address
/// @return flag Mining permission flag
function checkMinter(address account) external view returns (uint) {
return _flags[account];
}
/// @dev Mint DCU
/// @param to Target address
/// @param value Mint amount
function mint(address to, uint value) external {
require(_flags[msg.sender] & 0x01 == 0x01, "DCU:!mint");
_mint(to, value);
}
/// @dev Burn DCU
/// @param from Target address
/// @param value Burn amount
function burn(address from, uint value) external {
require(_flags[msg.sender] & 0x02 == 0x02, "DCU:!burn");
_burn(from, value);
}
}
// File contracts/FortOptions.sol
// GPL-3.0-or-later
pragma solidity ^0.8.6;
/// @dev European option
contract FortOptions is ChainParameter, FortFrequentlyUsed, FortPriceAdapter, IFortOptions {
/// @dev Option structure
struct Option {
uint32 owner;
uint112 balance;
uint16 tokenIndex;
uint56 strikePrice;
bool orientation;
uint32 exerciseBlock;
}
// token registration information
struct TokenRegistration {
TokenConfig tokenConfig;
address tokenAddress;
}
// 64bits 1
int128 constant ONE = 0x10000000000000000;
// 64bits 50000
uint constant V50000 = 0x0C3500000000000000000;
// Proportion of option selling value, 10000 basis. 9500
uint constant SELL_RATE = 9500;
// Option array
Option[] _options;
// Registered account address mapping
mapping(address=>uint) _accountMapping;
// Registered accounts
address[] _accounts;
// token to index mapping
mapping(address=>uint) _tokenMapping;
// token registration information array
TokenRegistration[] _tokenRegistrations;
constructor() {
}
/// @dev To support open-zeppelin/upgrades
/// @param governance IFortGovernance implementation contract address
function initialize(address governance) public override {
super.initialize(governance);
_accounts.push();
}
/// @dev Register token information
/// @param tokenAddress Target token address, 0 means eth
/// @param tokenConfig token configuration
function register(address tokenAddress, TokenConfig calldata tokenConfig) external onlyGovernance {
// Get index + 1 by tokenAddress
uint index = _tokenMapping[tokenAddress];
// index == 0 means token not registered, add
if (index == 0) {
// Add TokenRegistration to array
_tokenRegistrations.push(TokenRegistration(tokenConfig, tokenAddress));
// Record index + 1
index = _tokenRegistrations.length;
require(index < 0x10000, "FO:too much tokenRegistrations");
_tokenMapping[tokenAddress] = index;
} else {
_tokenRegistrations[index - 1].tokenConfig = tokenConfig;
}
}
/// @dev Returns the share of the specified option for target address
/// @param index Index of the option
/// @param addr Target address
function balanceOf(uint index, address addr) external view override returns (uint) {
Option memory option = _options[index];
if (uint(option.owner) == getAccountIndex(addr)) {
return uint(option.balance);
}
return 0;
}
/// @dev Find the options of the target address (in reverse order)
/// @param start Find forward from the index corresponding to the given contract address
/// (excluding the record corresponding to start)
/// @param count Maximum number of records returned
/// @param maxFindCount Find records at most
/// @param owner Target address
/// @return optionArray Matched option array
function find(
uint start,
uint count,
uint maxFindCount,
address owner
) external view override returns (OptionView[] memory optionArray) {
optionArray = new OptionView[](count);
// Calculate search region
Option[] storage options = _options;
uint i = options.length;
uint end = 0;
if (start > 0) {
i = start;
}
if (i > maxFindCount) {
end = i - maxFindCount;
}
uint ownerIndex = getAccountIndex(owner);
// Loop lookup to write qualified records to the buffer
for (uint index = 0; index < count && i > end;) {
Option storage option = options[--i];
if (uint(option.owner) == ownerIndex) {
optionArray[index++] = _toOptionView(option, i);
}
}
}
/// @dev List options
/// @param offset Skip previous (offset) records
/// @param count Return (count) records
/// @param order Order. 0 reverse order, non-0 positive order
/// @return optionArray Matched option array
function list(
uint offset,
uint count,
uint order
) external view override returns (OptionView[] memory optionArray) {
// Load options
Option[] storage options = _options;
// Create result array
optionArray = new OptionView[](count);
uint length = options.length;
uint i = 0;
// Reverse order
if (order == 0) {
uint index = length - offset;
uint end = index > count ? index - count : 0;
while (index > end) {
Option storage option = options[--index];
optionArray[i++] = _toOptionView(option, index);
}
}
// Positive order
else {
uint index = offset;
uint end = index + count;
if (end > length) {
end = length;
}
while (index < end) {
optionArray[i++] = _toOptionView(options[index], index);
++index;
}
}
}
/// @dev Obtain the number of European options that have been opened
/// @return Number of European options opened
function getOptionCount() external view override returns (uint) {
return _options.length;
}
/// @dev Open option
/// @param tokenAddress Target token address, 0 means eth
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @param dcuAmount Amount of paid DCU
function open(
address tokenAddress,
uint strikePrice,
bool orientation,
uint exerciseBlock,
uint dcuAmount
) external payable override {
// _tokenMapping[tokenAddress] > 0 means token registered
uint tokenIndex = _tokenMapping[tokenAddress] - 1;
TokenConfig memory tokenConfig = _tokenRegistrations[tokenIndex].tokenConfig;
// 1. Query price from oracle
uint oraclePrice = _latestPrice(tokenConfig, msg.value, msg.sender);
// 2. Calculate the amount of option
uint amount = estimate(tokenAddress, oraclePrice, strikePrice, orientation, exerciseBlock, dcuAmount);
// 3. Open
// Emit open event
emit Open(_options.length, dcuAmount, msg.sender, amount);
// Add option to array
_options.push(Option(
//uint32 owner;
uint32(_addressIndex(msg.sender)),
//uint112 balance;
_toUInt112(amount),
uint16(tokenIndex),
//uint56 strikePrice;
_encodeFloat(strikePrice),
//bool orientation;
orientation,
//uint32 exerciseBlock;
uint32(exerciseBlock)
));
// 4. Burn DCU
DCU(DCU_TOKEN_ADDRESS).burn(msg.sender, dcuAmount);
}
/// @dev Estimate the amount of option
/// @param tokenAddress Target token address, 0 means eth
/// @param oraclePrice Current price from oracle
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @param dcuAmount Amount of paid DCU
/// @return amount Amount of option
function estimate(
address tokenAddress,
uint oraclePrice,
uint strikePrice,
bool orientation,
uint exerciseBlock,
uint dcuAmount
) public view override returns (uint amount) {
return _estimate(
_tokenRegistrations[_tokenMapping[tokenAddress] - 1].tokenConfig,
oraclePrice,
strikePrice,
orientation,
exerciseBlock,
dcuAmount
);
}
/// @dev Exercise option
/// @param index Index of option
/// @param amount Amount of option to exercise
function exercise(uint index, uint amount) external payable override {
// 1. Load the option
Option storage option = _options[index];
address owner = _accounts[uint(option.owner)];
uint strikePrice = _decodeFloat(option.strikePrice);
bool orientation = option.orientation;
uint exerciseBlock = uint(option.exerciseBlock);
TokenConfig memory tokenConfig = _tokenRegistrations[option.tokenIndex].tokenConfig;
// TODO:
//require(block.number >= exerciseBlock, "FEO:at maturity");
// 2. Deduct the specified amount
option.balance = _toUInt112(uint(option.balance) - amount);
// 3. Find the price by specified block from oracle
uint oraclePrice = _findPrice(tokenConfig, exerciseBlock, msg.value, msg.sender);
// 4. Calculate the number of DCU that can be obtained
uint gain = 0;
// Call option
if (orientation) {
// Win
if (oraclePrice > strikePrice) {
gain = amount * (oraclePrice - strikePrice) / USDT_BASE;
}
}
// Put option
else {
// Win
if (oraclePrice < strikePrice) {
gain = amount * (strikePrice - oraclePrice) / USDT_BASE;
}
}
// 5. If win, mint DCU to user
if (gain > 0) {
DCU(DCU_TOKEN_ADDRESS).mint(owner, gain);
}
// emit Exercise event
emit Exercise(index, amount, owner, gain);
}
/// @dev Sell option
/// @param index Index of option
/// @param amount Amount of option to sell
function sell(uint index, uint amount) external payable override {
// Sell formula: vt=Max(ct(T,K)*0.95, 0).
// ct(K,T) Is the price of option present
// Note: No less than 1% condition
// 1. Load the option
Option storage option = _options[index];
address owner = _accounts[uint(option.owner)];
require(owner == msg.sender, "FO:not owner");
uint strikePrice = _decodeFloat(option.strikePrice);
bool orientation = option.orientation;
uint exerciseBlock = uint(option.exerciseBlock);
TokenConfig memory tokenConfig = _tokenRegistrations[option.tokenIndex].tokenConfig;
// 2. Deduct the specified amount
option.balance = _toUInt112(uint(option.balance) - amount);
// 3. Query price from oracle
uint oraclePrice = _latestPrice(tokenConfig, msg.value, msg.sender);
// 4. Calculate option price
uint dcuAmount = amount * _calcV(
tokenConfig,
oraclePrice,
strikePrice,
orientation,
exerciseBlock
) * SELL_RATE / (USDT_BASE * 0x27100000000000000000);
//(USDT_BASE * 10000 << 64);
if (dcuAmount > 0) {
DCU(DCU_TOKEN_ADDRESS).mint(msg.sender, dcuAmount);
}
// emit Sell event
emit Sell(index, amount, msg.sender, dcuAmount);
}
/// @dev Calculate option price
/// @param oraclePrice Current price from oracle
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @return v Option price. Need to divide (USDT_BASE << 64)
function calcV(
address tokenAddress,
uint oraclePrice,
uint strikePrice,
bool orientation,
uint exerciseBlock
) public view override returns (uint v) {
return _calcV(
_tokenRegistrations[_tokenMapping[tokenAddress] - 1].tokenConfig,
oraclePrice,
strikePrice,
orientation,
exerciseBlock
);
}
/// @dev Gets the address corresponding to the given index number
/// @param index The index number of the specified address
/// @return The address corresponding to the given index number
function indexAddress(uint index) public view returns (address) {
return _accounts[index];
}
/// @dev Gets the registration index number of the specified address
/// @param addr Destination address
/// @return 0 means nonexistent, non-0 means index number
function getAccountIndex(address addr) public view returns (uint) {
return _accountMapping[addr];
}
/// @dev Get the length of registered account array
/// @return The length of registered account array
function getAccountCount() external view returns (uint) {
return _accounts.length;
}
// Convert to OptionView
function _toOptionView(
Option storage option,
uint index
) private view returns (OptionView memory) {
return OptionView(
index,
_tokenRegistrations[option.tokenIndex].tokenAddress,
_decodeFloat(option.strikePrice),
option.orientation,
uint(option.exerciseBlock),
option.balance,
_accounts[uint(option.owner)]
);
}
// Convert 18 decimal points to 64 binary points
function _d18TOb64(uint v) private pure returns (int128) {
require(v < 0x6F05B59D3B200000000000000000000, "FEO:can't convert to 64bits");
return int128(int((v << 64) / 1 ether));
}
// Convert uint to int128
function _toInt128(uint v) private pure returns (int128) {
require(v < 0x80000000000000000000000000000000, "FEO:can't convert to int128");
return int128(int(v));
}
// Convert int128 to uint
function _toUInt(int128 v) private pure returns (uint) {
require(v >= 0, "FEO:can't convert to uint");
return uint(int(v));
}
// Convert uint to uint112
function _toUInt112(uint v) private pure returns (uint112) {
require(v < 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF,"HO:can't convert to uint112");
return uint112(v);
}
// Calculate standard normal distribution by table
function _snd(int128 x) private pure returns (int128) {
uint[28] memory table = [
/* */ ///////////////////// STANDARD NORMAL TABLE //////////////////////////
/* */ 0x174A15BF143412A8111C0F8F0E020C740AE6095807CA063B04AD031E018F0000, //
///// 0x2F8C2E0F2C912B1229922811268F250B23872202207D1EF61D6F1BE61A5D18D8, //
/* */ 0x2F8C2E0F2C912B1229922811268F250B23872202207D1EF61D6F1BE61A5D18D4, //
/* */ 0x46A2453C43D4426B41003F943E263CB63B4539D3385F36EA357333FB32823108, //
/* */ 0x5C0D5AC5597B582F56E05590543E52EA5194503C4EE24D874C294ACA49694807, //
/* */ 0x6F6C6E466D1F6BF56AC9699B686A6738660364CC6392625761195FD95E975D53, //
/* */ 0x807E7F7F7E7D7D797C737B6A7A5F79517841772F761A750373E972CD71AF708E, //
/* */ 0x8F2A8E518D768C998BB98AD789F2890B88218736864785568463836E8276817B, //
/* */ 0x9B749AC19A0B9953989997DD971E965D959A94D4940C9342927591A690D49000, //
///// 0xA57CA4ECA459A3C4A32EA295A1FAA15CA0BDA01C9F789ED29E2A9D809CD39C25, //
/* */ 0xA57CA4ECA459A3C4A32EA295A1FAA15DA0BDA01C9F789ED29E2A9D809CD39C25, //
/* */ 0xAD78AD07AC93AC1EABA7AB2EAAB3AA36A9B8A937A8B5A830A7AAA721A697A60B, //
/* */ 0xB3AAB353B2FAB2A0B245B1E7B189B128B0C6B062AFFDAF96AF2DAEC2AE56ADE8, //
/* */ 0xB859B818B7D6B793B74EB708B6C0B678B62EB5E2B595B547B4F7B4A6B454B400, //
/* */ 0xBBCDBB9EBB6EBB3CBB0ABAD7BAA2BA6DBA36B9FFB9C6B98CB951B915B8D8B899, //
/* */ 0xBE49BE27BE05BDE2BDBEBD99BD74BD4DBD26BCFEBCD5BCACBC81BC56BC29BBFC, //
/* */ 0xC006BFEEBFD7BFBEBFA5BF8CBF72BF57BF3CBF20BF03BEE6BEC8BEA9BE8ABE69, //
/* */ 0xC135C126C116C105C0F4C0E3C0D1C0BFC0ACC099C086C072C05DC048C032C01C, //
/* */ 0xC200C1F5C1EBC1E0C1D5C1C9C1BEC1B1C1A5C198C18BC17EC170C162C154C145, //
/* */ 0xC283C27CC275C26EC267C260C258C250C248C240C238C22FC226C21DC213C20A, //
/* */ 0xC2D6C2D2C2CDC2C9C2C5C2C0C2BBC2B6C2B1C2ACC2A7C2A1C29BC295C28FC289, //
/* */ 0xC309C306C304C301C2FEC2FCC2F9C2F6C2F2C2EFC2ECC2E8C2E5C2E1C2DEC2DA, //
/* */ 0xC328C326C325C323C321C320C31EC31CC31AC318C316C314C312C310C30EC30B, //
/* */ 0xC33AC339C338C337C336C335C334C333C332C331C330C32EC32DC32CC32AC329, //
/* */ 0xC344C343C343C342C342C341C341C340C33FC33FC33EC33DC33DC33CC33BC33A, //
/* */ 0xC34AC349C349C349C348C348C348C348C347C347C346C346C346C345C345C344, //
/* */ 0xC34DC34DC34CC34CC34CC34CC34CC34CC34BC34BC34BC34BC34BC34AC34AC34A, //
/* */ 0xC34EC34EC34EC34EC34EC34EC34EC34EC34EC34EC34DC34DC34DC34DC34DC34D, //
/* */ 0xC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34EC34E, //
/* */ 0xC350C350C350C350C350C350C34FC34FC34FC34FC34FC34FC34FC34FC34FC34F //
/* */ //////////////////// MADE IN CHINA 2021-08-24 ////////////////////////
];
uint ux = uint(int(x < 0 ? -x : x)) * 100;
uint i = ux >> 64;
uint v = V50000;
if (i < 447) {
v = uint((table[i >> 4] >> ((i & 0xF) << 4)) & 0xFFFF) << 64;
v = (
(
(
(uint((table[(i + 1) >> 4] >> (((i + 1) & 0xF) << 4)) & 0xFFFF) << 64)
- v
) * (ux & 0xFFFFFFFFFFFFFFFF) //(ux - (i << 64))
) >> 64
) + v;
}
if (x > 0) {
v = V50000 + v;
} else {
v = V50000 - v;
}
return int128(int(v / 100000));
}
/// @dev Estimate the amount of option
/// @param oraclePrice Current price from oracle
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @param dcuAmount Amount of paid DCU
/// @return amount Amount of option
function _estimate(
TokenConfig memory tokenConfig,
uint oraclePrice,
uint strikePrice,
bool orientation,
uint exerciseBlock,
uint dcuAmount
) private view returns (uint amount) {
require(exerciseBlock > block.number + MIN_PERIOD, "FEO:exerciseBlock too small");
// 1. Calculate option price
uint v = _calcV(
tokenConfig,
oraclePrice,
strikePrice,
orientation,
exerciseBlock
);
// 2. Correct option price
if (orientation) {
//v = _calcVc(config, oraclePrice, T, strikePrice);
// Vc>=S0*1%; Vp>=K*1%
// require(v * 100 >> 64 >= oraclePrice, "FEO:vc must greater than S0*1%");
if (v * 100 >> 64 < oraclePrice) {
v = oraclePrice * 0x10000000000000000 / 100;
}
} else {
//v = _calcVp(config, oraclePrice, T, strikePrice);
// Vc>=S0*1%; Vp>=K*1%
// require(v * 100 >> 64 >= strikePrice, "FEO:vp must greater than K*1%");
if (v * 100 >> 64 < strikePrice) {
v = strikePrice * 0x10000000000000000 / 100;
}
}
// 3. Calculate the amount of option
amount = (USDT_BASE << 64) * dcuAmount / v;
}
/// @dev Calculate option price
/// @param oraclePrice Current price from oracle
/// @param strikePrice The exercise price set by the user. During settlement, the system will compare the
/// current price of the subject matter with the exercise price to calculate the user's profit and loss
/// @param orientation true: call, false: put
/// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
/// recorded in the system using the block number
/// @return v Option price. Need to divide (USDT_BASE << 64)
function _calcV(
TokenConfig memory tokenConfig,
uint oraclePrice,
uint strikePrice,
bool orientation,
uint exerciseBlock
) private view returns (uint v) {
// Convert the total time according to the average block out time
uint T = (exerciseBlock - block.number) * BLOCK_TIME;
v = orientation
? _calcVc(tokenConfig, oraclePrice, T, strikePrice)
: _calcVp(tokenConfig, oraclePrice, T, strikePrice);
}
// Calculate option price for call
function _calcVc(TokenConfig memory tokenConfig, uint S0, uint T, uint K) private pure returns (uint vc) {
int128 sigmaSQ_T = _d18TOb64(uint(tokenConfig.sigmaSQ) * T);
int128 miu_T = _toInt128(uint(tokenConfig.miuLong) * T);
int128 sigma_t = ABDKMath64x64.sqrt(sigmaSQ_T);
int128 D1 = _D1(S0, K, sigmaSQ_T, miu_T);
int128 d = ABDKMath64x64.div(D1, sigma_t);
uint left = _toUInt(ABDKMath64x64.mul(
//ABDKMath64x64.exp(miu_T),
// Use approximate calculation method: x*(1+rt)
// by chenf 2021-12-28 15:27
miu_T + ONE,
ABDKMath64x64.sub(
ONE,
_snd(ABDKMath64x64.sub(d, sigma_t))
)
)) * S0;
uint right = _toUInt(ABDKMath64x64.sub(ONE, _snd(d))) * K;
vc = left > right ? left - right : 0;
}
// Calculate option price for put
function _calcVp(TokenConfig memory tokenConfig, uint S0, uint T, uint K) private pure returns (uint vp) {
int128 sigmaSQ_T = _d18TOb64(uint(tokenConfig.sigmaSQ) * T);
int128 miu_T = _toInt128(uint(tokenConfig.miuShort) * T);
int128 sigma_t = ABDKMath64x64.sqrt(sigmaSQ_T);
int128 D1 = _D1(S0, K, sigmaSQ_T, miu_T);
int128 d = ABDKMath64x64.div(D1, sigma_t);
uint left = _toUInt(_snd(d)) * K;
uint right = _toUInt(ABDKMath64x64.mul(
//ABDKMath64x64.exp(miu_T),
// Use approximate calculation method: x*(1+rt)
// by chenf 2021-12-28 15:27
miu_T + ONE,
_snd(ABDKMath64x64.sub(d, sigma_t))
)) * S0;
vp = left > right ? left - right : 0;
}
// d1 in formula, Because didn't divide by σ, So it's named D1
function _D1(uint S0, uint K, int128 sigmaSQ_T, int128 miu_T) private pure returns (int128) {
//require(K < 0x1000000000000000000000000000000000000000000000000, "FEO:K can't ROL 64bits");
return
ABDKMath64x64.sub(
ABDKMath64x64.add(
ABDKMath64x64.ln(_toInt128(K * 0x10000000000000000 / S0)),
sigmaSQ_T >> 1
),
miu_T
);
}
/// @dev Encode the uint value as a floating-point representation in the form of fraction * 16 ^ exponent
/// @param value Destination uint value
/// @return float format
function _encodeFloat(uint value) private pure returns (uint56) {
uint exponent = 0;
while (value > 0x3FFFFFFFFFFFF) {
value >>= 4;
++exponent;
}
return uint56((value << 6) | exponent);
}
/// @dev Decode the floating-point representation of fraction * 16 ^ exponent to uint
/// @param floatValue fraction value
/// @return decode format
function _decodeFloat(uint56 floatValue) private pure returns (uint) {
return (uint(floatValue) >> 6) << ((uint(floatValue) & 0x3F) << 2);
}
/// @dev Gets the index number of the specified address. If it does not exist, register
/// @param addr Destination address
/// @return The index number of the specified address
function _addressIndex(address addr) private returns (uint) {
uint index = _accountMapping[addr];
if (index == 0) {
// If it exceeds the maximum number that 32 bits can store, you can't continue to register a new account.
// If you need to support a new account, you need to update the contract
require((_accountMapping[addr] = index = _accounts.length) < 0x100000000, "HO:!accounts");
_accounts.push(addr);
}
return index;
}
}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"Exercise","inputs":[{"type":"uint256","name":"index","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"uint256","name":"gain","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceChanged","inputs":[{"type":"address","name":"oldGovernance","internalType":"address","indexed":false},{"type":"address","name":"newGovernance","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"Open","inputs":[{"type":"uint256","name":"index","internalType":"uint256","indexed":false},{"type":"uint256","name":"dcuAmount","internalType":"uint256","indexed":false},{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Sell","inputs":[{"type":"uint256","name":"index","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"uint256","name":"dcuAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"_governance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"v","internalType":"uint256"}],"name":"calcV","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"oraclePrice","internalType":"uint256"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"}],"name":"estimate","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"oraclePrice","internalType":"uint256"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"dcuAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"exercise","inputs":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"optionArray","internalType":"struct IFortOptions.OptionView[]","components":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]}],"name":"find","inputs":[{"type":"uint256","name":"start","internalType":"uint256"},{"type":"uint256","name":"count","internalType":"uint256"},{"type":"uint256","name":"maxFindCount","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAccountCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAccountIndex","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getOptionCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"indexAddress","inputs":[{"type":"uint256","name":"index","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"governance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"optionArray","internalType":"struct IFortOptions.OptionView[]","components":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]}],"name":"list","inputs":[{"type":"uint256","name":"offset","internalType":"uint256"},{"type":"uint256","name":"count","internalType":"uint256"},{"type":"uint256","name":"order","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"open","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"dcuAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"register","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"tuple","name":"tokenConfig","internalType":"struct FortPriceAdapter.TokenConfig","components":[{"type":"uint16","name":"channelId","internalType":"uint16"},{"type":"uint16","name":"pairIndex","internalType":"uint16"},{"type":"uint64","name":"sigmaSQ","internalType":"uint64"},{"type":"uint64","name":"miuLong","internalType":"uint64"},{"type":"uint64","name":"miuShort","internalType":"uint64"}]}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sell","inputs":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"update","inputs":[{"type":"address","name":"newGovernance","internalType":"address"}]}]
Contract Creation Code
0x608060405234801561001057600080fd5b50613b7d806100206000396000f3fe6080604052600436106100f35760003560e01c80639ba63e9e1161008a578063c4d66de811610059578063c4d66de8146102cc578063d79875eb146102ec578063e983b85c146102ff578063ee1ca9601461031f57600080fd5b80639ba63e9e1461024c578063a6bd54271461028f578063a86265bb146102a4578063a98e4e77146102b757600080fd5b80632fee0638116100c65780632fee0638146101cc5780633656eec2146101ec57806375e5da181461020c578063860506801461022c57600080fd5b806301b5ff8f146100f857806319aeb4901461012b5780631c1b8772146101585780631c2f3e3d1461017a575b600080fd5b34801561010457600080fd5b50610118610113366004613179565b610332565b6040519081526020015b60405180910390f35b34801561013757600080fd5b5061014b6101463660046131cb565b610414565b60405161012291906131f7565b34801561016457600080fd5b50610178610173366004613294565b610609565b005b34801561018657600080fd5b506000546101a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610122565b3480156101d857600080fd5b506101786101e73660046132b1565b6106dd565b3480156101f857600080fd5b50610118610207366004613310565b610a85565b34801561021857600080fd5b50610118610227366004613340565b610bc7565b34801561023857600080fd5b506101a761024736600461339c565b610cab565b34801561025857600080fd5b50610118610267366004613294565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b34801561029b57600080fd5b50600354610118565b6101786102b23660046133b5565b610ce8565b3480156102c357600080fd5b50600554610118565b3480156102d857600080fd5b506101786102e7366004613294565b611076565b6101786102fa3660046133b5565b61108e565b34801561030b57600080fd5b5061014b61031a3660046133d7565b61142d565b61017861032d366004613418565b6115ce565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461040a9060079061036b90600190613499565b8154811061037b5761037b6134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528686868661199e565b9695505050505050565b606060038367ffffffffffffffff811115610431576104316134df565b6040519080825280602002602001820160405280156104d057816020015b6104bd6040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b81526020019060019003908161044f5790505b508154909250600084810361057c5760006104eb8884613499565b905060008782116104fd576000610507565b6105078883613499565b90505b808211156105755760008561051e8461350e565b93508381548110610531576105316134b0565b90600052602060002001905061054781846119db565b878561055281613543565b965081518110610564576105646134b0565b60200260200101819052505061050a565b50506105ff565b866000610589888361357b565b9050838111156105965750825b808210156105fc576105c38583815481106105b3576105b36134b0565b90600052602060002001836119db565b86846105ce81613543565b9550815181106105e0576105e06134b0565b6020026020010181905250816105f590613543565b9150610596565b50505b5050509392505050565b61061281611bab565b8073ffffffffffffffffffffffffffffffffffffffff1663746b56f96040518163ffffffff1660e01b815260040160c060405180830381865afa15801561065d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106819190613593565b6002805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560018054979092169616959095179094555050505050565b600080546040517f91e1472b000000000000000000000000000000000000000000000000000000008152336004820152602481019290925273ffffffffffffffffffffffffffffffffffffffff16906391e1472b90604401602060405180830381865afa158015610752573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610776919061361a565b6107e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526006602052604081205490819003610a4657600760405180604001604052808480360381019061082d91906136ac565b815273ffffffffffffffffffffffffffffffffffffffff8681166020928301528354600180820186556000958652948390208451805160029093029091018054828601516040840151606085015160809095015161ffff9687167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009094169390931762010000969092168602919091177fffffffffffffffffffffffff00000000000000000000000000000000ffffffff1664010000000067ffffffffffffffff928316027fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff16176c0100000000000000000000000094821694909402939093177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000939091169290920291909117815593909201519290930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016929093169190911790915560075491508110610a19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f464f3a746f6f206d75636820746f6b656e526567697374726174696f6e73000060448201526064016107d8565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600660205260409020819055505050565b816007610a54600184613499565b81548110610a6457610a646134b0565b60009182526020909120600290910201610a7e8282613751565b5050505050565b60008060038481548110610a9b57610a9b6134b0565b600091825260208083206040805160c081018252939091015463ffffffff808216855264010000000082046dffffffffffffffffffffffffffff16858501527201000000000000000000000000000000000000820461ffff168584015274010000000000000000000000000000000000000000820466ffffffffffffff1660608601527b01000000000000000000000000000000000000000000000000000000820460ff16151560808601527c01000000000000000000000000000000000000000000000000000000009091041660a084015273ffffffffffffffffffffffffffffffffffffffff87168452600490915290912054909150815163ffffffff1603610bbb57602001516dffffffffffffffffffffffffffff169050610bc1565b60009150505b92915050565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260066020526040812054610ca090600790610c0090600190613499565b81548110610c1057610c106134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528787878787611d60565b979650505050505050565b600060058281548110610cc057610cc06134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1692915050565b600060038381548110610cfd57610cfd6134b0565b600091825260208220018054600580549294509163ffffffff909116908110610d2857610d286134b0565b6000918252602082200154835473ffffffffffffffffffffffffffffffffffffffff9091169250740100000000000000000000000000000000000000009004600681901c6603ffffffffffff1660029190911b60fc161b8354600780549293507b01000000000000000000000000000000000000000000000000000000820460ff16927c0100000000000000000000000000000000000000000000000000000000830463ffffffff169260009291720100000000000000000000000000000000000090910461ffff16908110610e0057610e006134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c010000000000000000000000008404811660608401527401000000000000000000000000000000000000000090930490921660808201528754909250610eb291610ead918a916dffffffffffffffffffffffffffff910416613499565b611e92565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000610f0382843433611f0f565b905060008415610f445785821115610f3f57670de0b6b3a7640000610f288784613499565b610f32908b6138d7565b610f3c9190613943565b90505b610f76565b85821015610f7657670de0b6b3a7640000610f5f8388613499565b610f69908b6138d7565b610f739190613943565b90505b8015611009576001546040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610ff057600080fd5b505af1158015611004573d6000803e3d6000fd5b505050505b604080518b8152602081018b905273ffffffffffffffffffffffffffffffffffffffff891691810191909152606081018290527f72db4df7c54a4e3502d596213c9116f06a327af3a139e03e5fb36fa5a09ecb9e906080015b60405180910390a150505050505050505050565b61107f81611ffb565b50600580546001018155600052565b6000600383815481106110a3576110a36134b0565b600091825260208220018054600580549294509163ffffffff9091169081106110ce576110ce6134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905033811461115b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f464f3a6e6f74206f776e6572000000000000000000000000000000000000000060448201526064016107d8565b815460078054740100000000000000000000000000000000000000008304600681901c6603ffffffffffff1660029190911b60fc161b927b01000000000000000000000000000000000000000000000000000000810460ff16927c0100000000000000000000000000000000000000000000000000000000820463ffffffff16926000927201000000000000000000000000000000000000900461ffff16908110611208576112086134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c0100000000000000000000000084048116606084015274010000000000000000000000000000000000000000909304909216608082015287549092506112b591610ead918a916dffffffffffffffffffffffffffff910416613499565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000611305823433612111565b90506000611325670de0b6b3a764000069271000000000000000006138d7565b61251c61133585858a8a8a61199e565b61133f908c6138d7565b61134991906138d7565b6113539190613943565b905080156113e6576001546040517f40c10f190000000000000000000000000000000000000000000000000000000081523360048201526024810183905273ffffffffffffffffffffffffffffffffffffffff909116906340c10f1990604401600060405180830381600087803b1580156113cd57600080fd5b505af11580156113e1573d6000803e3d6000fd5b505050505b604080518b8152602081018b90523391810191909152606081018290527fb69e24112cb4483e933dc3bda2d474e8d511e1d7058a983fe98a7d5d78fb974390608001611062565b60608367ffffffffffffffff811115611448576114486134df565b6040519080825280602002602001820160405280156114e757816020015b6114d46040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b8152602001906001900390816114665790505b506003805491925090600087156114fc578791505b858211156115115761150e8683613499565b90505b73ffffffffffffffffffffffffffffffffffffffff8516600090815260046020526040812054905b888110801561154757508284115b156115c1576000856115588661350e565b9550858154811061156b5761156b6134b0565b6000918252602090912001805490915063ffffffff168390036115bb5761159281866119db565b878361159d81613543565b9450815181106115af576115af6134b0565b60200260200101819052505b50611539565b5050505050949350505050565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461160090600190613499565b9050600060078281548110611617576116176134b0565b600091825260208083206040805160a08101825260029094029091015461ffff8082168552620100008204169284019290925267ffffffffffffffff64010000000083048116918401919091526c010000000000000000000000008204811660608401527401000000000000000000000000000000000000000090910416608082015291506116a7823433612111565b905060006116b989838a8a8a8a610bc7565b60035460408051918252602082018890523390820152606081018290529091507f39b02417d6826eadf5421ac690db0dd00065bb988a2f8cd32341c68d74d36beb9060800160405180910390a160036040518060c0016040528061171c336121fa565b63ffffffff16815260200161173084611e92565b6dffffffffffffffffffffffffffff1681526020018661ffff1681526020016117588b612337565b66ffffffffffffff90811682528a151560208084019190915263ffffffff8b81166040948501528554600180820188556000978852968390208651910180549387015187870151606089015160808a015160a0909a015186167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9a15157b01000000000000000000000000000000000000000000000000000000029a909a167affffffffffffffffffffffffffffffffffffffffffffffffffffff9190981674010000000000000000000000000000000000000000027fffffffffff00000000000000ffffffffffffffffffffffffffffffffffffffff61ffff909316720100000000000000000000000000000000000002929092167fffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff909416640100000000027fffffffffffffffffffffffffffff000000000000000000000000000000000000909816959096169490941795909517169290921792909217919091169190911792909217909155905490517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201526024810187905273ffffffffffffffffffffffffffffffffffffffff90911690639dc29fac90604401600060405180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b50505050505050505050505050565b60008060036119ad4385613499565b6119b791906138d7565b9050836119cf576119ca8787838861236f565b610ca0565b610ca08787838861246e565b611a496040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040805160e0810190915282815283546007805460208401927201000000000000000000000000000000000000900461ffff16908110611a8b57611a8b6134b0565b60009182526020918290206002918202016001015473ffffffffffffffffffffffffffffffffffffffff16835286549290910191740100000000000000000000000000000000000000009004600681901c6603ffffffffffff16911b60fc161b8152845460ff7b010000000000000000000000000000000000000000000000000000008204161515602083015263ffffffff7c01000000000000000000000000000000000000000000000000000000008204811660408401526dffffffffffffffffffffffffffff640100000000830416606084015260058054608090940193909291909116908110611b8057611b806134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1690529392505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633811480611c6257506040517f91e1472b0000000000000000000000000000000000000000000000000000000081523360048201526000602482015273ffffffffffffffffffffffffffffffffffffffff8216906391e1472b90604401602060405180830381865afa158015611c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c62919061361a565b611cc8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064016107d8565b6040805173ffffffffffffffffffffffffffffffffffffffff8084168252841660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a150600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000611d6d600a4361357b565b8311611dd5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a6578657263697365426c6f636b20746f6f20736d616c6c000000000060448201526064016107d8565b6000611de4888888888861199e565b90508415611e2957866040611dfa8360646138d7565b901c1015611e24576064611e1788680100000000000000006138d7565b611e219190613943565b90505b611e61565b856040611e378360646138d7565b901c1015611e61576064611e5487680100000000000000006138d7565b611e5e9190613943565b90505b80611e7c846f0de0b6b3a764000000000000000000006138d7565b611e869190613943565b98975050505050505050565b60006dffffffffffffffffffffffffffff8210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f484f3a63616e277420636f6e7665727420746f2075696e74313132000000000060448201526064016107d8565b5090565b60025484516020860151600092839273ffffffffffffffffffffffffffffffffffffffff9091169163594e25b591879161ffff91821691611f50911661253f565b89886040518663ffffffff1660e01b8152600401611f7194939291906139b9565b60006040518083038185885af1158015611f8f573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611fd691908101906139ff565b905061040a81600181518110611fee57611fee6134b0565b6020026020010151612586565b60005473ffffffffffffffffffffffffffffffffffffffff161561207b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f466f72743a21696e697469616c697a650000000000000000000000000000000060448201526064016107d8565b604080516000815273ffffffffffffffffffffffffffffffffffffffff831660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a1600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60025483516020850151600092839273ffffffffffffffffffffffffffffffffffffffff909116916354a80f0491879161ffff91821691612152911661253f565b6001886040518663ffffffff1660e01b815260040161217494939291906139b9565b60006040518083038185885af1158015612192573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526121d991908101906139ff565b90506121f181600181518110611fee57611fee6134b0565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260046020526040812054808203610bc1575060055473ffffffffffffffffffffffffffffffffffffffff8316600090815260046020526040902081905564010000000081106122c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f484f3a216163636f756e7473000000000000000000000000000000000000000060448201526064016107d8565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851617905592915050565b6000805b6603ffffffffffff8311156123615760049290921c9161235a81613543565b905061233b565b60069290921b909117919050565b60008061239484876040015167ffffffffffffffff1661238f91906138d7565b6125b9565b905060006123ba85886080015167ffffffffffffffff166123b591906138d7565b61264a565b905060006123c7836126c5565b905060006123d7888786866126e7565b905060006123e5828461272b565b90506000876123fb6123f6846127ab565b612d12565b61240591906138d7565b905060008a61243a6123f6612423680100000000000000008a613a99565b612435612430888b612d88565b6127ab565b612ddc565b61244491906138d7565b905080821161245457600061245e565b61245e8183613499565b9c9b505050505050505050505050565b60008061248e84876040015167ffffffffffffffff1661238f91906138d7565b905060006124af85886060015167ffffffffffffffff166123b591906138d7565b905060006124bc836126c5565b905060006124cc888786866126e7565b905060006124da828461272b565b90506000896125176123f66124f86801000000000000000089613a99565b61243568010000000000000000612512612430898c612d88565b612d88565b61252191906138d7565b905060008861243a6123f668010000000000000000612512876127ab565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110612575576125756134b0565b602002602001018181525050919050565b60008161259d670de0b6b3a76400006107d06138d7565b6125af90670de0b6b3a76400006138d7565b610bc19190613943565b60006f06f05b59d3b2000000000000000000008210612634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20363462697473000000000060448201526064016107d8565b610bc1670de0b6b3a7640000604084901b613943565b60006f800000000000000000000000000000008210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20696e74313238000000000060448201526064016107d8565b60008082600f0b12156126d757600080fd5b610bc1604083600f0b901b612e33565b60006121f16127256127186127138861270989680100000000000000006138d7565b6123b59190613943565b612fb6565b600186600f0b901d612ff0565b83612d88565b600081600f0b60000361273d57600080fd5b600082600f0b604085600f0b901b8161275857612758613914565b0590507fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113155b6127a457600080fd5b9392505050565b6000806040518061038001604052807f174a15bf143412a8111c0f8f0e020c740ae6095807ca063b04ad031e018f000081526020017f2f8c2e0f2c912b1229922811268f250b23872202207d1ef61d6f1be61a5d18d481526020017f46a2453c43d4426b41003f943e263cb63b4539d3385f36ea357333fb3282310881526020017f5c0d5ac5597b582f56e05590543e52ea5194503c4ee24d874c294aca4969480781526020017f6f6c6e466d1f6bf56ac9699b686a6738660364cc6392625761195fd95e975d5381526020017f807e7f7f7e7d7d797c737b6a7a5f79517841772f761a750373e972cd71af708e81526020017f8f2a8e518d768c998bb98ad789f2890b88218736864785568463836e8276817b81526020017f9b749ac19a0b9953989997dd971e965d959a94d4940c9342927591a690d4900081526020017fa57ca4eca459a3c4a32ea295a1faa15da0bda01c9f789ed29e2a9d809cd39c2581526020017fad78ad07ac93ac1eaba7ab2eaab3aa36a9b8a937a8b5a830a7aaa721a697a60b81526020017fb3aab353b2fab2a0b245b1e7b189b128b0c6b062affdaf96af2daec2ae56ade881526020017fb859b818b7d6b793b74eb708b6c0b678b62eb5e2b595b547b4f7b4a6b454b40081526020017fbbcdbb9ebb6ebb3cbb0abad7baa2ba6dba36b9ffb9c6b98cb951b915b8d8b89981526020017fbe49be27be05bde2bdbebd99bd74bd4dbd26bcfebcd5bcacbc81bc56bc29bbfc81526020017fc006bfeebfd7bfbebfa5bf8cbf72bf57bf3cbf20bf03bee6bec8bea9be8abe6981526020017fc135c126c116c105c0f4c0e3c0d1c0bfc0acc099c086c072c05dc048c032c01c81526020017fc200c1f5c1ebc1e0c1d5c1c9c1bec1b1c1a5c198c18bc17ec170c162c154c14581526020017fc283c27cc275c26ec267c260c258c250c248c240c238c22fc226c21dc213c20a81526020017fc2d6c2d2c2cdc2c9c2c5c2c0c2bbc2b6c2b1c2acc2a7c2a1c29bc295c28fc28981526020017fc309c306c304c301c2fec2fcc2f9c2f6c2f2c2efc2ecc2e8c2e5c2e1c2dec2da81526020017fc328c326c325c323c321c320c31ec31cc31ac318c316c314c312c310c30ec30b81526020017fc33ac339c338c337c336c335c334c333c332c331c330c32ec32dc32cc32ac32981526020017fc344c343c343c342c342c341c341c340c33fc33fc33ec33dc33dc33cc33bc33a81526020017fc34ac349c349c349c348c348c348c348c347c347c346c346c346c345c345c34481526020017fc34dc34dc34cc34cc34cc34cc34cc34cc34bc34bc34bc34bc34bc34ac34ac34a81526020017fc34ec34ec34ec34ec34ec34ec34ec34ec34ec34ec34dc34dc34dc34dc34dc34d81526020017fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34ec34e81526020017fc350c350c350c350c350c350c34fc34fc34fc34fc34fc34fc34fc34fc34fc34f815250905060008084600f0b12612bf45783612bfd565b612bfd84613b09565b612c0b90600f0b60646138d7565b9050604081901c69c35000000000000000006101bf821015612cc7576040600483600f16901b85600485901c601c8110612c4757612c476134b0565b602002015161ffff911c16901b905080604067ffffffffffffffff851682826004612c7388600161357b565b600f16901b896004612c868a600161357b565b901c601c8110612c9857612c986134b0565b6020020151901c61ffff16901b612caf9190613499565b612cb991906138d7565b612cc492911c61357b565b90505b600086600f0b1315612cee57612ce78169c350000000000000000061357b565b9050612d05565b612d028169c3500000000000000000613499565b90505b61040a620186a082613943565b60008082600f0b1215612d81576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f46454f3a63616e277420636f6e7665727420746f2075696e740000000000000060448201526064016107d8565b50600f0b90565b6000600f82810b9084900b037fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b6000600f83810b9083900b0260401d7fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b600081600003612e4557506000919050565b8160017001000000000000000000000000000000008210612e6b5760809190911c9060401b5b680100000000000000008210612e865760409190911c9060201b5b6401000000008210612e9d5760209190911c9060101b5b620100008210612eb25760109190911c9060081b5b6101008210612ec65760089190911c9060041b5b60108210612ed95760049190911c9060021b5b60088210612ee55760011b5b6001818581612ef657612ef6613914565b048201901c90506001818581612f0e57612f0e613914565b048201901c90506001818581612f2657612f26613914565b048201901c90506001818581612f3e57612f3e613914565b048201901c90506001818581612f5657612f56613914565b048201901c90506001818581612f6e57612f6e613914565b048201901c90506001818581612f8657612f86613914565b048201901c90506000818581612f9e57612f9e613914565b049050808210612fae57806121f1565b509392505050565b60008082600f0b13612fc757600080fd5b6080612fd283613044565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b6000600f83810b9083900b017fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b60008082600f0b1361305557600080fd5b6000600f83900b680100000000000000008112613074576040918201911d5b6401000000008112613088576020918201911d5b62010000811261309a576010918201911d5b61010081126130ab576008918201911d5b601081126130bb576004918201911d5b600481126130cb576002918201911d5b600281126130da576001820191505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820160401b600f85900b607f8490031b6780000000000000005b600081131561313b5790800260ff81901c8281029390930192607f011c9060011d613115565b509095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461316857600080fd5b50565b801515811461316857600080fd5b600080600080600060a0868803121561319157600080fd5b853561319c81613146565b9450602086013593506040860135925060608601356131ba8161316b565b949793965091946080013592915050565b6000806000606084860312156131e057600080fd5b505081359360208301359350604090920135919050565b602080825282518282018190526000919060409081850190868401855b82811015613287578151805185528681015173ffffffffffffffffffffffffffffffffffffffff9081168887015286820151878701526060808301511515908701526080808301519087015260a0808301519087015260c091820151169085015260e09093019290850190600101613214565b5091979650505050505050565b6000602082840312156132a657600080fd5b81356127a481613146565b60008082840360c08112156132c557600080fd5b83356132d081613146565b925060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561330257600080fd5b506020830190509250929050565b6000806040838503121561332357600080fd5b82359150602083013561333581613146565b809150509250929050565b60008060008060008060c0878903121561335957600080fd5b863561336481613146565b9550602087013594506040870135935060608701356133828161316b565b9598949750929560808101359460a0909101359350915050565b6000602082840312156133ae57600080fd5b5035919050565b600080604083850312156133c857600080fd5b50508035926020909101359150565b600080600080608085870312156133ed57600080fd5b843593506020850135925060408501359150606085013561340d81613146565b939692955090935050565b600080600080600060a0868803121561343057600080fd5b853561343b81613146565b94506020860135935060408601356134528161316b565b94979396509394606081013594506080013592915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156134ab576134ab61346a565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008161351d5761351d61346a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036135745761357461346a565b5060010190565b6000821982111561358e5761358e61346a565b500190565b60008060008060008060c087890312156135ac57600080fd5b86516135b781613146565b60208801519096506135c881613146565b60408801519095506135d981613146565b60608801519094506135ea81613146565b60808801519093506135fb81613146565b60a088015190925061360c81613146565b809150509295509295509295565b60006020828403121561362c57600080fd5b81516127a48161316b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561367e5761367e6134df565b604052919050565b61ffff8116811461316857600080fd5b67ffffffffffffffff8116811461316857600080fd5b600060a082840312156136be57600080fd5b60405160a0810181811067ffffffffffffffff821117156136e1576136e16134df565b60405282356136ef81613686565b815260208301356136ff81613686565b6020820152604083013561371281613696565b6040820152606083013561372581613696565b6060820152608083013561373881613696565b60808201529392505050565b60008135610bc181613696565b813561375c81613686565b61ffff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00008216178355602084013561379a81613686565b63ffff00008160101b16905080837fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000084161717845560408501356137dd81613696565b6bffffffffffffffff000000008160201b169050837fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008416179350808483171785556060860135925061382f83613696565b73ffffffffffffffff0000000000000000000000008360601b167fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff851683178217178555505050506138d361388660808401613744565b82547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b7bffffffffffffffff000000000000000000000000000000000000000016178255565b5050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561390f5761390f61346a565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082613979577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600081518084526020808501945080840160005b838110156139ae57815187529582019590820190600101613992565b509495945050505050565b8481526080602082015260006139d2608083018661397e565b905083604083015273ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b60006020808385031215613a1257600080fd5b825167ffffffffffffffff80821115613a2a57600080fd5b818501915085601f830112613a3e57600080fd5b815181811115613a5057613a506134df565b8060051b9150613a61848301613637565b8181529183018401918481019088841115613a7b57600080fd5b938501935b83851015611e8657845182529385019390850190613a80565b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff03821381151615613acc57613acc61346a565b827fffffffffffffffffffffffffffffffff80000000000000000000000000000000038212811615613b0057613b0061346a565b50019392505050565b600081600f0b7fffffffffffffffffffffffffffffffff800000000000000000000000000000008103613b3e57613b3e61346a565b6000039291505056fea26469706673582212202e5d1bffb2d1f4cc06b752e3336fdf4c4c7540782e640328bfdc72297c30cc3a64736f6c634300080d0033
Deployed ByteCode
0x6080604052600436106100f35760003560e01c80639ba63e9e1161008a578063c4d66de811610059578063c4d66de8146102cc578063d79875eb146102ec578063e983b85c146102ff578063ee1ca9601461031f57600080fd5b80639ba63e9e1461024c578063a6bd54271461028f578063a86265bb146102a4578063a98e4e77146102b757600080fd5b80632fee0638116100c65780632fee0638146101cc5780633656eec2146101ec57806375e5da181461020c578063860506801461022c57600080fd5b806301b5ff8f146100f857806319aeb4901461012b5780631c1b8772146101585780631c2f3e3d1461017a575b600080fd5b34801561010457600080fd5b50610118610113366004613179565b610332565b6040519081526020015b60405180910390f35b34801561013757600080fd5b5061014b6101463660046131cb565b610414565b60405161012291906131f7565b34801561016457600080fd5b50610178610173366004613294565b610609565b005b34801561018657600080fd5b506000546101a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610122565b3480156101d857600080fd5b506101786101e73660046132b1565b6106dd565b3480156101f857600080fd5b50610118610207366004613310565b610a85565b34801561021857600080fd5b50610118610227366004613340565b610bc7565b34801561023857600080fd5b506101a761024736600461339c565b610cab565b34801561025857600080fd5b50610118610267366004613294565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b34801561029b57600080fd5b50600354610118565b6101786102b23660046133b5565b610ce8565b3480156102c357600080fd5b50600554610118565b3480156102d857600080fd5b506101786102e7366004613294565b611076565b6101786102fa3660046133b5565b61108e565b34801561030b57600080fd5b5061014b61031a3660046133d7565b61142d565b61017861032d366004613418565b6115ce565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461040a9060079061036b90600190613499565b8154811061037b5761037b6134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528686868661199e565b9695505050505050565b606060038367ffffffffffffffff811115610431576104316134df565b6040519080825280602002602001820160405280156104d057816020015b6104bd6040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b81526020019060019003908161044f5790505b508154909250600084810361057c5760006104eb8884613499565b905060008782116104fd576000610507565b6105078883613499565b90505b808211156105755760008561051e8461350e565b93508381548110610531576105316134b0565b90600052602060002001905061054781846119db565b878561055281613543565b965081518110610564576105646134b0565b60200260200101819052505061050a565b50506105ff565b866000610589888361357b565b9050838111156105965750825b808210156105fc576105c38583815481106105b3576105b36134b0565b90600052602060002001836119db565b86846105ce81613543565b9550815181106105e0576105e06134b0565b6020026020010181905250816105f590613543565b9150610596565b50505b5050509392505050565b61061281611bab565b8073ffffffffffffffffffffffffffffffffffffffff1663746b56f96040518163ffffffff1660e01b815260040160c060405180830381865afa15801561065d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106819190613593565b6002805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560018054979092169616959095179094555050505050565b600080546040517f91e1472b000000000000000000000000000000000000000000000000000000008152336004820152602481019290925273ffffffffffffffffffffffffffffffffffffffff16906391e1472b90604401602060405180830381865afa158015610752573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610776919061361a565b6107e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526006602052604081205490819003610a4657600760405180604001604052808480360381019061082d91906136ac565b815273ffffffffffffffffffffffffffffffffffffffff8681166020928301528354600180820186556000958652948390208451805160029093029091018054828601516040840151606085015160809095015161ffff9687167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009094169390931762010000969092168602919091177fffffffffffffffffffffffff00000000000000000000000000000000ffffffff1664010000000067ffffffffffffffff928316027fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff16176c0100000000000000000000000094821694909402939093177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000939091169290920291909117815593909201519290930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016929093169190911790915560075491508110610a19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f464f3a746f6f206d75636820746f6b656e526567697374726174696f6e73000060448201526064016107d8565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600660205260409020819055505050565b816007610a54600184613499565b81548110610a6457610a646134b0565b60009182526020909120600290910201610a7e8282613751565b5050505050565b60008060038481548110610a9b57610a9b6134b0565b600091825260208083206040805160c081018252939091015463ffffffff808216855264010000000082046dffffffffffffffffffffffffffff16858501527201000000000000000000000000000000000000820461ffff168584015274010000000000000000000000000000000000000000820466ffffffffffffff1660608601527b01000000000000000000000000000000000000000000000000000000820460ff16151560808601527c01000000000000000000000000000000000000000000000000000000009091041660a084015273ffffffffffffffffffffffffffffffffffffffff87168452600490915290912054909150815163ffffffff1603610bbb57602001516dffffffffffffffffffffffffffff169050610bc1565b60009150505b92915050565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260066020526040812054610ca090600790610c0090600190613499565b81548110610c1057610c106134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528787878787611d60565b979650505050505050565b600060058281548110610cc057610cc06134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1692915050565b600060038381548110610cfd57610cfd6134b0565b600091825260208220018054600580549294509163ffffffff909116908110610d2857610d286134b0565b6000918252602082200154835473ffffffffffffffffffffffffffffffffffffffff9091169250740100000000000000000000000000000000000000009004600681901c6603ffffffffffff1660029190911b60fc161b8354600780549293507b01000000000000000000000000000000000000000000000000000000820460ff16927c0100000000000000000000000000000000000000000000000000000000830463ffffffff169260009291720100000000000000000000000000000000000090910461ffff16908110610e0057610e006134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c010000000000000000000000008404811660608401527401000000000000000000000000000000000000000090930490921660808201528754909250610eb291610ead918a916dffffffffffffffffffffffffffff910416613499565b611e92565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000610f0382843433611f0f565b905060008415610f445785821115610f3f57670de0b6b3a7640000610f288784613499565b610f32908b6138d7565b610f3c9190613943565b90505b610f76565b85821015610f7657670de0b6b3a7640000610f5f8388613499565b610f69908b6138d7565b610f739190613943565b90505b8015611009576001546040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610ff057600080fd5b505af1158015611004573d6000803e3d6000fd5b505050505b604080518b8152602081018b905273ffffffffffffffffffffffffffffffffffffffff891691810191909152606081018290527f72db4df7c54a4e3502d596213c9116f06a327af3a139e03e5fb36fa5a09ecb9e906080015b60405180910390a150505050505050505050565b61107f81611ffb565b50600580546001018155600052565b6000600383815481106110a3576110a36134b0565b600091825260208220018054600580549294509163ffffffff9091169081106110ce576110ce6134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905033811461115b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f464f3a6e6f74206f776e6572000000000000000000000000000000000000000060448201526064016107d8565b815460078054740100000000000000000000000000000000000000008304600681901c6603ffffffffffff1660029190911b60fc161b927b01000000000000000000000000000000000000000000000000000000810460ff16927c0100000000000000000000000000000000000000000000000000000000820463ffffffff16926000927201000000000000000000000000000000000000900461ffff16908110611208576112086134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c0100000000000000000000000084048116606084015274010000000000000000000000000000000000000000909304909216608082015287549092506112b591610ead918a916dffffffffffffffffffffffffffff910416613499565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000611305823433612111565b90506000611325670de0b6b3a764000069271000000000000000006138d7565b61251c61133585858a8a8a61199e565b61133f908c6138d7565b61134991906138d7565b6113539190613943565b905080156113e6576001546040517f40c10f190000000000000000000000000000000000000000000000000000000081523360048201526024810183905273ffffffffffffffffffffffffffffffffffffffff909116906340c10f1990604401600060405180830381600087803b1580156113cd57600080fd5b505af11580156113e1573d6000803e3d6000fd5b505050505b604080518b8152602081018b90523391810191909152606081018290527fb69e24112cb4483e933dc3bda2d474e8d511e1d7058a983fe98a7d5d78fb974390608001611062565b60608367ffffffffffffffff811115611448576114486134df565b6040519080825280602002602001820160405280156114e757816020015b6114d46040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b8152602001906001900390816114665790505b506003805491925090600087156114fc578791505b858211156115115761150e8683613499565b90505b73ffffffffffffffffffffffffffffffffffffffff8516600090815260046020526040812054905b888110801561154757508284115b156115c1576000856115588661350e565b9550858154811061156b5761156b6134b0565b6000918252602090912001805490915063ffffffff168390036115bb5761159281866119db565b878361159d81613543565b9450815181106115af576115af6134b0565b60200260200101819052505b50611539565b5050505050949350505050565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461160090600190613499565b9050600060078281548110611617576116176134b0565b600091825260208083206040805160a08101825260029094029091015461ffff8082168552620100008204169284019290925267ffffffffffffffff64010000000083048116918401919091526c010000000000000000000000008204811660608401527401000000000000000000000000000000000000000090910416608082015291506116a7823433612111565b905060006116b989838a8a8a8a610bc7565b60035460408051918252602082018890523390820152606081018290529091507f39b02417d6826eadf5421ac690db0dd00065bb988a2f8cd32341c68d74d36beb9060800160405180910390a160036040518060c0016040528061171c336121fa565b63ffffffff16815260200161173084611e92565b6dffffffffffffffffffffffffffff1681526020018661ffff1681526020016117588b612337565b66ffffffffffffff90811682528a151560208084019190915263ffffffff8b81166040948501528554600180820188556000978852968390208651910180549387015187870151606089015160808a015160a0909a015186167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9a15157b01000000000000000000000000000000000000000000000000000000029a909a167affffffffffffffffffffffffffffffffffffffffffffffffffffff9190981674010000000000000000000000000000000000000000027fffffffffff00000000000000ffffffffffffffffffffffffffffffffffffffff61ffff909316720100000000000000000000000000000000000002929092167fffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff909416640100000000027fffffffffffffffffffffffffffff000000000000000000000000000000000000909816959096169490941795909517169290921792909217919091169190911792909217909155905490517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201526024810187905273ffffffffffffffffffffffffffffffffffffffff90911690639dc29fac90604401600060405180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b50505050505050505050505050565b60008060036119ad4385613499565b6119b791906138d7565b9050836119cf576119ca8787838861236f565b610ca0565b610ca08787838861246e565b611a496040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040805160e0810190915282815283546007805460208401927201000000000000000000000000000000000000900461ffff16908110611a8b57611a8b6134b0565b60009182526020918290206002918202016001015473ffffffffffffffffffffffffffffffffffffffff16835286549290910191740100000000000000000000000000000000000000009004600681901c6603ffffffffffff16911b60fc161b8152845460ff7b010000000000000000000000000000000000000000000000000000008204161515602083015263ffffffff7c01000000000000000000000000000000000000000000000000000000008204811660408401526dffffffffffffffffffffffffffff640100000000830416606084015260058054608090940193909291909116908110611b8057611b806134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1690529392505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633811480611c6257506040517f91e1472b0000000000000000000000000000000000000000000000000000000081523360048201526000602482015273ffffffffffffffffffffffffffffffffffffffff8216906391e1472b90604401602060405180830381865afa158015611c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c62919061361a565b611cc8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064016107d8565b6040805173ffffffffffffffffffffffffffffffffffffffff8084168252841660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a150600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000611d6d600a4361357b565b8311611dd5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a6578657263697365426c6f636b20746f6f20736d616c6c000000000060448201526064016107d8565b6000611de4888888888861199e565b90508415611e2957866040611dfa8360646138d7565b901c1015611e24576064611e1788680100000000000000006138d7565b611e219190613943565b90505b611e61565b856040611e378360646138d7565b901c1015611e61576064611e5487680100000000000000006138d7565b611e5e9190613943565b90505b80611e7c846f0de0b6b3a764000000000000000000006138d7565b611e869190613943565b98975050505050505050565b60006dffffffffffffffffffffffffffff8210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f484f3a63616e277420636f6e7665727420746f2075696e74313132000000000060448201526064016107d8565b5090565b60025484516020860151600092839273ffffffffffffffffffffffffffffffffffffffff9091169163594e25b591879161ffff91821691611f50911661253f565b89886040518663ffffffff1660e01b8152600401611f7194939291906139b9565b60006040518083038185885af1158015611f8f573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611fd691908101906139ff565b905061040a81600181518110611fee57611fee6134b0565b6020026020010151612586565b60005473ffffffffffffffffffffffffffffffffffffffff161561207b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f466f72743a21696e697469616c697a650000000000000000000000000000000060448201526064016107d8565b604080516000815273ffffffffffffffffffffffffffffffffffffffff831660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a1600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60025483516020850151600092839273ffffffffffffffffffffffffffffffffffffffff909116916354a80f0491879161ffff91821691612152911661253f565b6001886040518663ffffffff1660e01b815260040161217494939291906139b9565b60006040518083038185885af1158015612192573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526121d991908101906139ff565b90506121f181600181518110611fee57611fee6134b0565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260046020526040812054808203610bc1575060055473ffffffffffffffffffffffffffffffffffffffff8316600090815260046020526040902081905564010000000081106122c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f484f3a216163636f756e7473000000000000000000000000000000000000000060448201526064016107d8565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851617905592915050565b6000805b6603ffffffffffff8311156123615760049290921c9161235a81613543565b905061233b565b60069290921b909117919050565b60008061239484876040015167ffffffffffffffff1661238f91906138d7565b6125b9565b905060006123ba85886080015167ffffffffffffffff166123b591906138d7565b61264a565b905060006123c7836126c5565b905060006123d7888786866126e7565b905060006123e5828461272b565b90506000876123fb6123f6846127ab565b612d12565b61240591906138d7565b905060008a61243a6123f6612423680100000000000000008a613a99565b612435612430888b612d88565b6127ab565b612ddc565b61244491906138d7565b905080821161245457600061245e565b61245e8183613499565b9c9b505050505050505050505050565b60008061248e84876040015167ffffffffffffffff1661238f91906138d7565b905060006124af85886060015167ffffffffffffffff166123b591906138d7565b905060006124bc836126c5565b905060006124cc888786866126e7565b905060006124da828461272b565b90506000896125176123f66124f86801000000000000000089613a99565b61243568010000000000000000612512612430898c612d88565b612d88565b61252191906138d7565b905060008861243a6123f668010000000000000000612512876127ab565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110612575576125756134b0565b602002602001018181525050919050565b60008161259d670de0b6b3a76400006107d06138d7565b6125af90670de0b6b3a76400006138d7565b610bc19190613943565b60006f06f05b59d3b2000000000000000000008210612634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20363462697473000000000060448201526064016107d8565b610bc1670de0b6b3a7640000604084901b613943565b60006f800000000000000000000000000000008210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20696e74313238000000000060448201526064016107d8565b60008082600f0b12156126d757600080fd5b610bc1604083600f0b901b612e33565b60006121f16127256127186127138861270989680100000000000000006138d7565b6123b59190613943565b612fb6565b600186600f0b901d612ff0565b83612d88565b600081600f0b60000361273d57600080fd5b600082600f0b604085600f0b901b8161275857612758613914565b0590507fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113155b6127a457600080fd5b9392505050565b6000806040518061038001604052807f174a15bf143412a8111c0f8f0e020c740ae6095807ca063b04ad031e018f000081526020017f2f8c2e0f2c912b1229922811268f250b23872202207d1ef61d6f1be61a5d18d481526020017f46a2453c43d4426b41003f943e263cb63b4539d3385f36ea357333fb3282310881526020017f5c0d5ac5597b582f56e05590543e52ea5194503c4ee24d874c294aca4969480781526020017f6f6c6e466d1f6bf56ac9699b686a6738660364cc6392625761195fd95e975d5381526020017f807e7f7f7e7d7d797c737b6a7a5f79517841772f761a750373e972cd71af708e81526020017f8f2a8e518d768c998bb98ad789f2890b88218736864785568463836e8276817b81526020017f9b749ac19a0b9953989997dd971e965d959a94d4940c9342927591a690d4900081526020017fa57ca4eca459a3c4a32ea295a1faa15da0bda01c9f789ed29e2a9d809cd39c2581526020017fad78ad07ac93ac1eaba7ab2eaab3aa36a9b8a937a8b5a830a7aaa721a697a60b81526020017fb3aab353b2fab2a0b245b1e7b189b128b0c6b062affdaf96af2daec2ae56ade881526020017fb859b818b7d6b793b74eb708b6c0b678b62eb5e2b595b547b4f7b4a6b454b40081526020017fbbcdbb9ebb6ebb3cbb0abad7baa2ba6dba36b9ffb9c6b98cb951b915b8d8b89981526020017fbe49be27be05bde2bdbebd99bd74bd4dbd26bcfebcd5bcacbc81bc56bc29bbfc81526020017fc006bfeebfd7bfbebfa5bf8cbf72bf57bf3cbf20bf03bee6bec8bea9be8abe6981526020017fc135c126c116c105c0f4c0e3c0d1c0bfc0acc099c086c072c05dc048c032c01c81526020017fc200c1f5c1ebc1e0c1d5c1c9c1bec1b1c1a5c198c18bc17ec170c162c154c14581526020017fc283c27cc275c26ec267c260c258c250c248c240c238c22fc226c21dc213c20a81526020017fc2d6c2d2c2cdc2c9c2c5c2c0c2bbc2b6c2b1c2acc2a7c2a1c29bc295c28fc28981526020017fc309c306c304c301c2fec2fcc2f9c2f6c2f2c2efc2ecc2e8c2e5c2e1c2dec2da81526020017fc328c326c325c323c321c320c31ec31cc31ac318c316c314c312c310c30ec30b81526020017fc33ac339c338c337c336c335c334c333c332c331c330c32ec32dc32cc32ac32981526020017fc344c343c343c342c342c341c341c340c33fc33fc33ec33dc33dc33cc33bc33a81526020017fc34ac349c349c349c348c348c348c348c347c347c346c346c346c345c345c34481526020017fc34dc34dc34cc34cc34cc34cc34cc34cc34bc34bc34bc34bc34bc34ac34ac34a81526020017fc34ec34ec34ec34ec34ec34ec34ec34ec34ec34ec34dc34dc34dc34dc34dc34d81526020017fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34ec34e81526020017fc350c350c350c350c350c350c34fc34fc34fc34fc34fc34fc34fc34fc34fc34f815250905060008084600f0b12612bf45783612bfd565b612bfd84613b09565b612c0b90600f0b60646138d7565b9050604081901c69c35000000000000000006101bf821015612cc7576040600483600f16901b85600485901c601c8110612c4757612c476134b0565b602002015161ffff911c16901b905080604067ffffffffffffffff851682826004612c7388600161357b565b600f16901b896004612c868a600161357b565b901c601c8110612c9857612c986134b0565b6020020151901c61ffff16901b612caf9190613499565b612cb991906138d7565b612cc492911c61357b565b90505b600086600f0b1315612cee57612ce78169c350000000000000000061357b565b9050612d05565b612d028169c3500000000000000000613499565b90505b61040a620186a082613943565b60008082600f0b1215612d81576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f46454f3a63616e277420636f6e7665727420746f2075696e740000000000000060448201526064016107d8565b50600f0b90565b6000600f82810b9084900b037fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b6000600f83810b9083900b0260401d7fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b600081600003612e4557506000919050565b8160017001000000000000000000000000000000008210612e6b5760809190911c9060401b5b680100000000000000008210612e865760409190911c9060201b5b6401000000008210612e9d5760209190911c9060101b5b620100008210612eb25760109190911c9060081b5b6101008210612ec65760089190911c9060041b5b60108210612ed95760049190911c9060021b5b60088210612ee55760011b5b6001818581612ef657612ef6613914565b048201901c90506001818581612f0e57612f0e613914565b048201901c90506001818581612f2657612f26613914565b048201901c90506001818581612f3e57612f3e613914565b048201901c90506001818581612f5657612f56613914565b048201901c90506001818581612f6e57612f6e613914565b048201901c90506001818581612f8657612f86613914565b048201901c90506000818581612f9e57612f9e613914565b049050808210612fae57806121f1565b509392505050565b60008082600f0b13612fc757600080fd5b6080612fd283613044565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b6000600f83810b9083900b017fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b60008082600f0b1361305557600080fd5b6000600f83900b680100000000000000008112613074576040918201911d5b6401000000008112613088576020918201911d5b62010000811261309a576010918201911d5b61010081126130ab576008918201911d5b601081126130bb576004918201911d5b600481126130cb576002918201911d5b600281126130da576001820191505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820160401b600f85900b607f8490031b6780000000000000005b600081131561313b5790800260ff81901c8281029390930192607f011c9060011d613115565b509095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461316857600080fd5b50565b801515811461316857600080fd5b600080600080600060a0868803121561319157600080fd5b853561319c81613146565b9450602086013593506040860135925060608601356131ba8161316b565b949793965091946080013592915050565b6000806000606084860312156131e057600080fd5b505081359360208301359350604090920135919050565b602080825282518282018190526000919060409081850190868401855b82811015613287578151805185528681015173ffffffffffffffffffffffffffffffffffffffff9081168887015286820151878701526060808301511515908701526080808301519087015260a0808301519087015260c091820151169085015260e09093019290850190600101613214565b5091979650505050505050565b6000602082840312156132a657600080fd5b81356127a481613146565b60008082840360c08112156132c557600080fd5b83356132d081613146565b925060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561330257600080fd5b506020830190509250929050565b6000806040838503121561332357600080fd5b82359150602083013561333581613146565b809150509250929050565b60008060008060008060c0878903121561335957600080fd5b863561336481613146565b9550602087013594506040870135935060608701356133828161316b565b9598949750929560808101359460a0909101359350915050565b6000602082840312156133ae57600080fd5b5035919050565b600080604083850312156133c857600080fd5b50508035926020909101359150565b600080600080608085870312156133ed57600080fd5b843593506020850135925060408501359150606085013561340d81613146565b939692955090935050565b600080600080600060a0868803121561343057600080fd5b853561343b81613146565b94506020860135935060408601356134528161316b565b94979396509394606081013594506080013592915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156134ab576134ab61346a565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008161351d5761351d61346a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036135745761357461346a565b5060010190565b6000821982111561358e5761358e61346a565b500190565b60008060008060008060c087890312156135ac57600080fd5b86516135b781613146565b60208801519096506135c881613146565b60408801519095506135d981613146565b60608801519094506135ea81613146565b60808801519093506135fb81613146565b60a088015190925061360c81613146565b809150509295509295509295565b60006020828403121561362c57600080fd5b81516127a48161316b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561367e5761367e6134df565b604052919050565b61ffff8116811461316857600080fd5b67ffffffffffffffff8116811461316857600080fd5b600060a082840312156136be57600080fd5b60405160a0810181811067ffffffffffffffff821117156136e1576136e16134df565b60405282356136ef81613686565b815260208301356136ff81613686565b6020820152604083013561371281613696565b6040820152606083013561372581613696565b6060820152608083013561373881613696565b60808201529392505050565b60008135610bc181613696565b813561375c81613686565b61ffff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00008216178355602084013561379a81613686565b63ffff00008160101b16905080837fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000084161717845560408501356137dd81613696565b6bffffffffffffffff000000008160201b169050837fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008416179350808483171785556060860135925061382f83613696565b73ffffffffffffffff0000000000000000000000008360601b167fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff851683178217178555505050506138d361388660808401613744565b82547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b7bffffffffffffffff000000000000000000000000000000000000000016178255565b5050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561390f5761390f61346a565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082613979577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600081518084526020808501945080840160005b838110156139ae57815187529582019590820190600101613992565b509495945050505050565b8481526080602082015260006139d2608083018661397e565b905083604083015273ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b60006020808385031215613a1257600080fd5b825167ffffffffffffffff80821115613a2a57600080fd5b818501915085601f830112613a3e57600080fd5b815181811115613a5057613a506134df565b8060051b9150613a61848301613637565b8181529183018401918481019088841115613a7b57600080fd5b938501935b83851015611e8657845182529385019390850190613a80565b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff03821381151615613acc57613acc61346a565b827fffffffffffffffffffffffffffffffff80000000000000000000000000000000038212811615613b0057613b0061346a565b50019392505050565b600081600f0b7fffffffffffffffffffffffffffffffff800000000000000000000000000000008103613b3e57613b3e61346a565b6000039291505056fea26469706673582212202e5d1bffb2d1f4cc06b752e3336fdf4c4c7540782e640328bfdc72297c30cc3a64736f6c634300080d0033