Address Details
contract

0x056d9AAC902cc2925BB31f6C516B1e1579c35df9

Contract Name
SlidingWindowOracle
Creator
0xe59f13–f1e2b8 at 0xced675–f10ea4
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
410,450 Transactions
Transfers
0 Transfers
Gas Used
19,482,822,544
Last Balance Update
15260127
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
SlidingWindowOracle




Optimization enabled
true
Compiler version
v0.6.6+commit.6c089d02




Optimization runs
999999
EVM Version
istanbul




Verified at
2022-06-16T19:09:40.085133Z

contracts/SlidingWindowOracle.sol

pragma solidity =0.6.6;

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "@uniswap/lib/contracts/libraries/FixedPoint.sol";

import "./libraries/SafeMath.sol";
import "./libraries/UniswapV2Library.sol";
import "./libraries/UniswapV2OracleLibrary.sol";

// sliding window oracle that uses observations collected over a window to provide moving price averages in the past
// `windowSize` with a precision of `windowSize / granularity`
// note this is a singleton oracle and only needs to be deployed once per desired parameters, which
// differs from the simple oracle which must be deployed once per pair.
contract SlidingWindowOracle {
    using FixedPoint for *;
    using SafeMath for uint256;

    struct Observation {
        uint256 timestamp;
        uint256 price0Cumulative;
        uint256 price1Cumulative;
    }

    address public immutable factory;
    // the desired amount of time over which the moving average should be computed, e.g. 24 hours
    uint256 public immutable windowSize;
    // the number of observations stored for each pair, i.e. how many price observations are stored for the window.
    // as granularity increases from 1, more frequent updates are needed, but moving averages become more precise.
    // averages are computed over intervals with sizes in the range:
    //   [windowSize - (windowSize / granularity) * 2, windowSize]
    // e.g. if the window size is 24 hours, and the granularity is 24, the oracle will return the average price for
    //   the period:
    //   [now - [22 hours, 24 hours], now]
    uint8 public immutable granularity;
    // this is redundant with granularity and windowSize, but stored for gas savings & informational purposes.
    uint256 public immutable periodSize;

    // mapping from pair address to a list of price observations of that pair
    mapping(address => Observation[]) public pairObservations;

    constructor(
        address factory_,
        uint256 windowSize_,
        uint8 granularity_
    ) public {
        require(granularity_ > 1, "SlidingWindowOracle: GRANULARITY");
        require(
            (periodSize = windowSize_ / granularity_) * granularity_ ==
                windowSize_,
            "SlidingWindowOracle: WINDOW_NOT_EVENLY_DIVISIBLE"
        );
        factory = factory_;
        windowSize = windowSize_;
        granularity = granularity_;
    }

    // returns the index of the observation corresponding to the given timestamp
    function observationIndexOf(uint256 timestamp)
        public
        view
        returns (uint8 index)
    {
        uint256 epochPeriod = timestamp / periodSize;
        return uint8(epochPeriod % granularity);
    }

    // returns the observation from the oldest epoch (at the beginning of the window) relative to the current time
    function getFirstObservationInWindow(address pair)
        private
        view
        returns (Observation storage firstObservation)
    {
        uint8 observationIndex = observationIndexOf(block.timestamp);
        // no overflow issue. if observationIndex + 1 overflows, result is still zero.
        uint8 firstObservationIndex = (observationIndex + 1) % granularity;
        firstObservation = pairObservations[pair][firstObservationIndex];
    }

    // update the cumulative price for the observation at the current timestamp. each observation is updated at most
    // once per epoch period.
    function update(address tokenA, address tokenB) external {
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);

        // populate the array with empty observations (first call only)
        for (uint256 i = pairObservations[pair].length; i < granularity; i++) {
            pairObservations[pair].push();
        }

        // get the observation for the current period
        uint8 observationIndex = observationIndexOf(block.timestamp);
        Observation storage observation = pairObservations[pair][
            observationIndex
        ];

        // we only want to commit updates once per period (i.e. windowSize / granularity)
        uint256 timeElapsed = block.timestamp - observation.timestamp;
        if (timeElapsed > periodSize) {
            (
                uint256 price0Cumulative,
                uint256 price1Cumulative,

            ) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
            observation.timestamp = block.timestamp;
            observation.price0Cumulative = price0Cumulative;
            observation.price1Cumulative = price1Cumulative;
        }
    }

    // given the cumulative prices of the start and end of a period, and the length of the period, compute the average
    // price in terms of how much amount out is received for the amount in
    function computeAmountOut(
        uint256 priceCumulativeStart,
        uint256 priceCumulativeEnd,
        uint256 timeElapsed,
        uint256 amountIn
    ) private pure returns (uint256 amountOut) {
        // overflow is desired.
        FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112(
            uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed)
        );
        amountOut = priceAverage.mul(amountIn).decode144();
    }

    // returns the amount out corresponding to the amount in for a given token using the moving average over the time
    // range [now - [windowSize, windowSize - periodSize * 2], now]
    // update must have been called for the bucket corresponding to timestamp `now - windowSize`
    function consult(
        address tokenIn,
        uint256 amountIn,
        address tokenOut
    ) external view returns (uint256 amountOut) {
        address pair = UniswapV2Library.pairFor(factory, tokenIn, tokenOut);
        Observation storage firstObservation = getFirstObservationInWindow(
            pair
        );

        uint256 timeElapsed = block.timestamp - firstObservation.timestamp;
        require(
            timeElapsed <= windowSize,
            "SlidingWindowOracle: MISSING_HISTORICAL_OBSERVATION"
        );
        // should never happen.
        require(
            timeElapsed >= windowSize - periodSize * 2,
            "SlidingWindowOracle: UNEXPECTED_TIME_ELAPSED"
        );

        (
            uint256 price0Cumulative,
            uint256 price1Cumulative,

        ) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
        (address token0, ) = UniswapV2Library.sortTokens(tokenIn, tokenOut);

        if (token0 == tokenIn) {
            return
                computeAmountOut(
                    firstObservation.price0Cumulative,
                    price0Cumulative,
                    timeElapsed,
                    amountIn
                );
        } else {
            return
                computeAmountOut(
                    firstObservation.price1Cumulative,
                    price1Cumulative,
                    timeElapsed,
                    amountIn
                );
        }
    }
}
        

/_uniswap/lib/contracts/libraries/Babylonian.sol

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.4.0;

// computes square roots using the babylonian method
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
library Babylonian {
    // credit for this implementation goes to
    // https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
    function sqrt(uint256 x) internal pure returns (uint256) {
        if (x == 0) return 0;
        // this block is equivalent to r = uint256(1) << (BitMath.mostSignificantBit(x) / 2);
        // however that code costs significantly more gas
        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 (r < r1 ? r : r1);
    }
}
          

/_uniswap/lib/contracts/libraries/BitMath.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.5.0;

library BitMath {
    // returns the 0 indexed position of the most significant bit of the input x
    // s.t. x >= 2**msb and x < 2**(msb+1)
    function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
        require(x > 0, 'BitMath::mostSignificantBit: zero');

        if (x >= 0x100000000000000000000000000000000) {
            x >>= 128;
            r += 128;
        }
        if (x >= 0x10000000000000000) {
            x >>= 64;
            r += 64;
        }
        if (x >= 0x100000000) {
            x >>= 32;
            r += 32;
        }
        if (x >= 0x10000) {
            x >>= 16;
            r += 16;
        }
        if (x >= 0x100) {
            x >>= 8;
            r += 8;
        }
        if (x >= 0x10) {
            x >>= 4;
            r += 4;
        }
        if (x >= 0x4) {
            x >>= 2;
            r += 2;
        }
        if (x >= 0x2) r += 1;
    }

    // returns the 0 indexed position of the least significant bit of the input x
    // s.t. (x & 2**lsb) != 0 and (x & (2**(lsb) - 1)) == 0)
    // i.e. the bit at the index is set and the mask of all lower bits is 0
    function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
        require(x > 0, 'BitMath::leastSignificantBit: zero');

        r = 255;
        if (x & uint128(-1) > 0) {
            r -= 128;
        } else {
            x >>= 128;
        }
        if (x & uint64(-1) > 0) {
            r -= 64;
        } else {
            x >>= 64;
        }
        if (x & uint32(-1) > 0) {
            r -= 32;
        } else {
            x >>= 32;
        }
        if (x & uint16(-1) > 0) {
            r -= 16;
        } else {
            x >>= 16;
        }
        if (x & uint8(-1) > 0) {
            r -= 8;
        } else {
            x >>= 8;
        }
        if (x & 0xf > 0) {
            r -= 4;
        } else {
            x >>= 4;
        }
        if (x & 0x3 > 0) {
            r -= 2;
        } else {
            x >>= 2;
        }
        if (x & 0x1 > 0) r -= 1;
    }
}
          

/_uniswap/lib/contracts/libraries/FixedPoint.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.4.0;

import './FullMath.sol';
import './Babylonian.sol';
import './BitMath.sol';

// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
library FixedPoint {
    // range: [0, 2**112 - 1]
    // resolution: 1 / 2**112
    struct uq112x112 {
        uint224 _x;
    }

    // range: [0, 2**144 - 1]
    // resolution: 1 / 2**112
    struct uq144x112 {
        uint256 _x;
    }

    uint8 public constant RESOLUTION = 112;
    uint256 public constant Q112 = 0x10000000000000000000000000000; // 2**112
    uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; // 2**224
    uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits)

    // encode a uint112 as a UQ112x112
    function encode(uint112 x) internal pure returns (uq112x112 memory) {
        return uq112x112(uint224(x) << RESOLUTION);
    }

    // encodes a uint144 as a UQ144x112
    function encode144(uint144 x) internal pure returns (uq144x112 memory) {
        return uq144x112(uint256(x) << RESOLUTION);
    }

    // decode a UQ112x112 into a uint112 by truncating after the radix point
    function decode(uq112x112 memory self) internal pure returns (uint112) {
        return uint112(self._x >> RESOLUTION);
    }

    // decode a UQ144x112 into a uint144 by truncating after the radix point
    function decode144(uq144x112 memory self) internal pure returns (uint144) {
        return uint144(self._x >> RESOLUTION);
    }

    // multiply a UQ112x112 by a uint, returning a UQ144x112
    // reverts on overflow
    function mul(uq112x112 memory self, uint256 y) internal pure returns (uq144x112 memory) {
        uint256 z = 0;
        require(y == 0 || (z = self._x * y) / y == self._x, 'FixedPoint::mul: overflow');
        return uq144x112(z);
    }

    // multiply a UQ112x112 by an int and decode, returning an int
    // reverts on overflow
    function muli(uq112x112 memory self, int256 y) internal pure returns (int256) {
        uint256 z = FullMath.mulDiv(self._x, uint256(y < 0 ? -y : y), Q112);
        require(z < 2**255, 'FixedPoint::muli: overflow');
        return y < 0 ? -int256(z) : int256(z);
    }

    // multiply a UQ112x112 by a UQ112x112, returning a UQ112x112
    // lossy
    function muluq(uq112x112 memory self, uq112x112 memory other) internal pure returns (uq112x112 memory) {
        if (self._x == 0 || other._x == 0) {
            return uq112x112(0);
        }
        uint112 upper_self = uint112(self._x >> RESOLUTION); // * 2^0
        uint112 lower_self = uint112(self._x & LOWER_MASK); // * 2^-112
        uint112 upper_other = uint112(other._x >> RESOLUTION); // * 2^0
        uint112 lower_other = uint112(other._x & LOWER_MASK); // * 2^-112

        // partial products
        uint224 upper = uint224(upper_self) * upper_other; // * 2^0
        uint224 lower = uint224(lower_self) * lower_other; // * 2^-224
        uint224 uppers_lowero = uint224(upper_self) * lower_other; // * 2^-112
        uint224 uppero_lowers = uint224(upper_other) * lower_self; // * 2^-112

        // so the bit shift does not overflow
        require(upper <= uint112(-1), 'FixedPoint::muluq: upper overflow');

        // this cannot exceed 256 bits, all values are 224 bits
        uint256 sum = uint256(upper << RESOLUTION) + uppers_lowero + uppero_lowers + (lower >> RESOLUTION);

        // so the cast does not overflow
        require(sum <= uint224(-1), 'FixedPoint::muluq: sum overflow');

        return uq112x112(uint224(sum));
    }

    // divide a UQ112x112 by a UQ112x112, returning a UQ112x112
    function divuq(uq112x112 memory self, uq112x112 memory other) internal pure returns (uq112x112 memory) {
        require(other._x > 0, 'FixedPoint::divuq: division by zero');
        if (self._x == other._x) {
            return uq112x112(uint224(Q112));
        }
        if (self._x <= uint144(-1)) {
            uint256 value = (uint256(self._x) << RESOLUTION) / other._x;
            require(value <= uint224(-1), 'FixedPoint::divuq: overflow');
            return uq112x112(uint224(value));
        }

        uint256 result = FullMath.mulDiv(Q112, self._x, other._x);
        require(result <= uint224(-1), 'FixedPoint::divuq: overflow');
        return uq112x112(uint224(result));
    }

    // returns a UQ112x112 which represents the ratio of the numerator to the denominator
    // can be lossy
    function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) {
        require(denominator > 0, 'FixedPoint::fraction: division by zero');
        if (numerator == 0) return FixedPoint.uq112x112(0);

        if (numerator <= uint144(-1)) {
            uint256 result = (numerator << RESOLUTION) / denominator;
            require(result <= uint224(-1), 'FixedPoint::fraction: overflow');
            return uq112x112(uint224(result));
        } else {
            uint256 result = FullMath.mulDiv(numerator, Q112, denominator);
            require(result <= uint224(-1), 'FixedPoint::fraction: overflow');
            return uq112x112(uint224(result));
        }
    }

    // take the reciprocal of a UQ112x112
    // reverts on overflow
    // lossy
    function reciprocal(uq112x112 memory self) internal pure returns (uq112x112 memory) {
        require(self._x != 0, 'FixedPoint::reciprocal: reciprocal of zero');
        require(self._x != 1, 'FixedPoint::reciprocal: overflow');
        return uq112x112(uint224(Q224 / self._x));
    }

    // square root of a UQ112x112
    // lossy between 0/1 and 40 bits
    function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) {
        if (self._x <= uint144(-1)) {
            return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112)));
        }

        uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x);
        safeShiftBits -= safeShiftBits % 2;
        return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2)));
    }
}
          

/_uniswap/lib/contracts/libraries/FullMath.sol

// SPDX-License-Identifier: CC-BY-4.0
pragma solidity >=0.4.0;

// taken from https://medium.com/coinmonks/math-in-solidity-part-3-percents-and-proportions-4db014e080b1
// license is CC-BY-4.0
library FullMath {
    function fullMul(uint256 x, uint256 y) internal pure returns (uint256 l, uint256 h) {
        uint256 mm = mulmod(x, y, uint256(-1));
        l = x * y;
        h = mm - l;
        if (mm < l) h -= 1;
    }

    function fullDiv(
        uint256 l,
        uint256 h,
        uint256 d
    ) private pure returns (uint256) {
        uint256 pow2 = d & -d;
        d /= pow2;
        l /= pow2;
        l += h * ((-pow2) / pow2 + 1);
        uint256 r = 1;
        r *= 2 - d * r;
        r *= 2 - d * r;
        r *= 2 - d * r;
        r *= 2 - d * r;
        r *= 2 - d * r;
        r *= 2 - d * r;
        r *= 2 - d * r;
        r *= 2 - d * r;
        return l * r;
    }

    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 d
    ) internal pure returns (uint256) {
        (uint256 l, uint256 h) = fullMul(x, y);

        uint256 mm = mulmod(x, y, d);
        if (mm > l) h -= 1;
        l -= mm;

        if (h == 0) return l / d;

        require(h < d, 'FullMath: FULLDIV_OVERFLOW');
        return fullDiv(l, h, d);
    }
}
          

/_uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol

pragma solidity >=0.5.0;

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}
          

/_uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol

pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}
          

/contracts/libraries/SafeMath.sol

pragma solidity =0.6.6;

// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)

library SafeMath {
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, "ds-math-add-overflow");
    }

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

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

/contracts/libraries/UniswapV2Library.sol

pragma solidity >=0.5.0;

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";

import "./SafeMath.sol";

library UniswapV2Library {
    using SafeMath for uint256;

    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB)
        internal
        pure
        returns (address token0, address token1)
    {
        require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
        (token0, token1) = tokenA < tokenB
            ? (tokenA, tokenB)
            : (tokenB, tokenA);
        require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
    }

    function pairFor(
        address factory,
        address tokenA,
        address tokenB
    ) internal view returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = IUniswapV2Factory(factory).getPair(token0, token1);
    }

    // fetches and sorts the reserves for a pair
    function getReserves(
        address factory,
        address tokenA,
        address tokenB
    ) internal view returns (uint256 reserveA, uint256 reserveB) {
        (address token0, ) = sortTokens(tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(
            pairFor(factory, tokenA, tokenB)
        ).getReserves();
        (reserveA, reserveB) = tokenA == token0
            ? (reserve0, reserve1)
            : (reserve1, reserve0);
    }

    // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) internal pure returns (uint256 amountB) {
        require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT");
        require(
            reserveA > 0 && reserveB > 0,
            "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
        );
        amountB = amountA.mul(reserveB) / reserveA;
    }

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountOut) {
        require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
        require(
            reserveIn > 0 && reserveOut > 0,
            "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
        );
        uint256 amountInWithFee = amountIn.mul(997);
        uint256 numerator = amountInWithFee.mul(reserveOut);
        uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountIn) {
        require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
        require(
            reserveIn > 0 && reserveOut > 0,
            "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
        );
        uint256 numerator = reserveIn.mul(amountOut).mul(1000);
        uint256 denominator = reserveOut.sub(amountOut).mul(997);
        amountIn = (numerator / denominator).add(1);
    }

    // performs chained getAmountOut calculations on any number of pairs
    function getAmountsOut(
        address factory,
        uint256 amountIn,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
        amounts = new uint256[](path.length);
        amounts[0] = amountIn;
        for (uint256 i; i < path.length - 1; i++) {
            (uint256 reserveIn, uint256 reserveOut) = getReserves(
                factory,
                path[i],
                path[i + 1]
            );
            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
        }
    }

    // performs chained getAmountIn calculations on any number of pairs
    function getAmountsIn(
        address factory,
        uint256 amountOut,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            (uint256 reserveIn, uint256 reserveOut) = getReserves(
                factory,
                path[i - 1],
                path[i]
            );
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
}
          

/contracts/libraries/UniswapV2OracleLibrary.sol

pragma solidity >=0.5.0;

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "@uniswap/lib/contracts/libraries/FixedPoint.sol";

// library with helper methods for oracles that are concerned with computing average prices
library UniswapV2OracleLibrary {
    using FixedPoint for *;

    // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
    function currentBlockTimestamp() internal view returns (uint32) {
        return uint32(block.timestamp % 2**32);
    }

    // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
    function currentCumulativePrices(address pair)
        internal
        view
        returns (
            uint256 price0Cumulative,
            uint256 price1Cumulative,
            uint32 blockTimestamp
        )
    {
        blockTimestamp = currentBlockTimestamp();
        price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
        price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();

        // if time has elapsed since the last update on the pair, mock the accumulated price values
        (
            uint112 reserve0,
            uint112 reserve1,
            uint32 blockTimestampLast
        ) = IUniswapV2Pair(pair).getReserves();
        if (blockTimestampLast != blockTimestamp) {
            // subtraction overflow is desired
            uint32 timeElapsed = blockTimestamp - blockTimestampLast;
            // addition overflow is desired
            // counterfactual
            price0Cumulative +=
                uint256(FixedPoint.fraction(reserve1, reserve0)._x) *
                timeElapsed;
            // counterfactual
            price1Cumulative +=
                uint256(FixedPoint.fraction(reserve0, reserve1)._x) *
                timeElapsed;
        }
    }
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"factory_","internalType":"address"},{"type":"uint256","name":"windowSize_","internalType":"uint256"},{"type":"uint8","name":"granularity_","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"consult","inputs":[{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenOut","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factory","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"granularity","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"index","internalType":"uint8"}],"name":"observationIndexOf","inputs":[{"type":"uint256","name":"timestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"uint256","name":"price0Cumulative","internalType":"uint256"},{"type":"uint256","name":"price1Cumulative","internalType":"uint256"}],"name":"pairObservations","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"periodSize","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"update","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"windowSize","inputs":[]}]
              

Contract Creation Code

0x61010060405234801561001157600080fd5b5060405161136b38038061136b8339818101604052606081101561003457600080fd5b5080516020820151604090920151909190600160ff82161161009d576040805162461bcd60e51b815260206004820181905260248201527f536c6964696e6757696e646f774f7261636c653a204752414e554c4152495459604482015290519081900360640190fd5b8160ff82168082816100ab57fe5b0460e081905202146100ee5760405162461bcd60e51b815260040180806020018281038252603081526020018061133b6030913960400191505060405180910390fd5b606083901b6001600160601b03191660805260a082905260f881901b7fff000000000000000000000000000000000000000000000000000000000000001660c05260e0516001600160a01b039093169260ff9091169061119c61019f600039806102f7528061059452806105e752806106475250806101f452806104d75280610613528061073d525080610218528061027e528061031b525080610242528061045c5280610485525061119c6000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063c45a01551161005b578063c45a01551461015f578063c640752d14610190578063dbaad32f146101cd578063e4463eb2146101ea57610088565b8063556f0dc71461008d5780638a14117a146100ab5780638c86f1e4146100c5578063bfcc8e4214610108575b600080fd5b6100956101f2565b6040805160ff9092168252519081900360200190f35b6100b3610216565b60408051918252519081900360200190f35b6100b3600480360360608110156100db57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020810135916040909101351661023a565b6101416004803603604081101561011e57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813516906020013561041b565b60408051938452602084019290925282820152519081900360600190f35b61016761045a565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6101cb600480360360408110156101a657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661047e565b005b610095600480360360208110156101e357600080fd5b50356105e2565b6100b3610645565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806102687f00000000000000000000000000000000000000000000000000000000000000008685610669565b905060006102758261072b565b805490915042037f00000000000000000000000000000000000000000000000000000000000000008111156102f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806110bd6033913960400191505060405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006002027f000000000000000000000000000000000000000000000000000000000000000003811015610393576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806110f0602c913960400191505060405180910390fd5b60008061039f856107d3565b509150915060006103b08a89610a5a565b5090508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610404576103f7856001015484868c610bad565b9650505050505050610414565b6103f7856002015483868c610bad565b9392505050565b6000602052816000526040600020818154811061043457fe5b600091825260209091206003909102018054600182015460029092015490935090915083565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006104ab7f00000000000000000000000000000000000000000000000000000000000000008484610669565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020549091505b7f000000000000000000000000000000000000000000000000000000000000000060ff168110156105365773ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604081208054600190810182559152016104d5565b506000610542426105e2565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604081208054929350909160ff841690811061057b57fe5b60009182526020909120600390910201805490915042037f00000000000000000000000000000000000000000000000000000000000000008111156105da576000806105c6866107d3565b504286556001860191909155600285015550505b505050505050565b6000807f0000000000000000000000000000000000000000000000000000000000000000838161060e57fe5b0490507f000000000000000000000000000000000000000000000000000000000000000060ff16818161063d57fe5b069392505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008060006106788585610a5a565b604080517fe6a4390500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80851660048301528084166024830152915193955091935088169163e6a4390591604480820192602092909190829003018186803b1580156106f557600080fd5b505afa158015610709573d6000803e3d6000fd5b505050506040513d602081101561071f57600080fd5b50519695505050505050565b600080610737426105e2565b905060007f000000000000000000000000000000000000000000000000000000000000000060ff168260010160ff168161076d57fe5b0690506000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208160ff16815481106107bc57fe5b906000526020600020906003020192505050919050565b60008060006107e0610c20565b90508373ffffffffffffffffffffffffffffffffffffffff16635909c0d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561082857600080fd5b505afa15801561083c573d6000803e3d6000fd5b505050506040513d602081101561085257600080fd5b5051604080517f5a3d5493000000000000000000000000000000000000000000000000000000008152905191945073ffffffffffffffffffffffffffffffffffffffff861691635a3d549391600480820192602092909190829003018186803b1580156108be57600080fd5b505afa1580156108d2573d6000803e3d6000fd5b505050506040513d60208110156108e857600080fd5b5051604080517f0902f1ac00000000000000000000000000000000000000000000000000000000815290519193506000918291829173ffffffffffffffffffffffffffffffffffffffff891691630902f1ac916004808301926060929190829003018186803b15801561095a57600080fd5b505afa15801561096e573d6000803e3d6000fd5b505050506040513d606081101561098457600080fd5b5080516020820151604090920151909450909250905063ffffffff80821690851614610a505780840363ffffffff81166109d16dffffffffffffffffffffffffffff808616908716610c2a565b600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1602870196508063ffffffff16610a29856dffffffffffffffffffffffffffff16856dffffffffffffffffffffffffffff16610c2a565b517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16029590950194505b5050509193909250565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061111c6025913960400191505060405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1610610b1c578284610b1f565b83835b909250905073ffffffffffffffffffffffffffffffffffffffff8216610ba657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b9250929050565b6000610bb7611097565b60405180602001604052808588880381610bcd57fe5b047bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690529050610c02610bfd8285610e3f565b610f02565b71ffffffffffffffffffffffffffffffffffff169695505050505050565b63ffffffff421690565b610c32611097565b60008211610c8b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806111416026913960400191505060405180910390fd5b82610ca55750604080516020810190915260008152610e39565b71ffffffffffffffffffffffffffffffffffff8311610d9057600082607085901b81610ccd57fe5b0490507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811115610d5b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4669786564506f696e743a3a6672616374696f6e3a206f766572666c6f770000604482015290519081900360640190fd5b6040518060200160405280827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16815250915050610e39565b6000610dac846e01000000000000000000000000000085610f09565b90507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811115610d5b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4669786564506f696e743a3a6672616374696f6e3a206f766572666c6f770000604482015290519081900360640190fd5b92915050565b610e476110a9565b6000821580610e8257505082517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682810290838281610e7f57fe5b04145b610eed57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4669786564506f696e743a3a6d756c3a206f766572666c6f7700000000000000604482015290519081900360640190fd5b60408051602081019091529081529392505050565b5160701c90565b6000806000610f188686610fdc565b9150915060008480610f2657fe5b868809905082811115610f3a576001820391505b918290039181610f5857848381610f4d57fe5b049350505050610414565b848210610fc657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f46756c6c4d6174683a2046554c4c4449565f4f564552464c4f57000000000000604482015290519081900360640190fd5b610fd1838387611027565b979650505050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8486099050838502925082810391508281101561101f576001820391505b509250929050565b6000818103821680838161103757fe5b04925080858161104357fe5b04945080816000038161105257fe5b60028581038087028203028087028203028087028203028087028203028087028203028087028203029586029003909402930460010193909302939093010292915050565b60408051602081019091526000815290565b604051806020016040528060008152509056fe536c6964696e6757696e646f774f7261636c653a204d495353494e475f484953544f524943414c5f4f42534552564154494f4e536c6964696e6757696e646f774f7261636c653a20554e45585045435445445f54494d455f454c4150534544556e697377617056324c6962726172793a204944454e544943414c5f4144445245535345534669786564506f696e743a3a6672616374696f6e3a206469766973696f6e206279207a65726fa2646970667358221220488b0fc6ff36f00947d18ab620c6ce82823b17e4475325b9008e26123d8ab00064736f6c63430006060033536c6964696e6757696e646f774f7261636c653a2057494e444f575f4e4f545f4556454e4c595f444956495349424c4500000000000000000000000062d5b84be28a183abb507e125b384122d2c25fae00000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000005

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063c45a01551161005b578063c45a01551461015f578063c640752d14610190578063dbaad32f146101cd578063e4463eb2146101ea57610088565b8063556f0dc71461008d5780638a14117a146100ab5780638c86f1e4146100c5578063bfcc8e4214610108575b600080fd5b6100956101f2565b6040805160ff9092168252519081900360200190f35b6100b3610216565b60408051918252519081900360200190f35b6100b3600480360360608110156100db57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020810135916040909101351661023a565b6101416004803603604081101561011e57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813516906020013561041b565b60408051938452602084019290925282820152519081900360600190f35b61016761045a565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6101cb600480360360408110156101a657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602001351661047e565b005b610095600480360360208110156101e357600080fd5b50356105e2565b6100b3610645565b7f000000000000000000000000000000000000000000000000000000000000000581565b7f000000000000000000000000000000000000000000000000000000000000025881565b6000806102687f00000000000000000000000062d5b84be28a183abb507e125b384122d2c25fae8685610669565b905060006102758261072b565b805490915042037f00000000000000000000000000000000000000000000000000000000000002588111156102f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806110bd6033913960400191505060405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000786002027f000000000000000000000000000000000000000000000000000000000000025803811015610393576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806110f0602c913960400191505060405180910390fd5b60008061039f856107d3565b509150915060006103b08a89610a5a565b5090508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610404576103f7856001015484868c610bad565b9650505050505050610414565b6103f7856002015483868c610bad565b9392505050565b6000602052816000526040600020818154811061043457fe5b600091825260209091206003909102018054600182015460029092015490935090915083565b7f00000000000000000000000062d5b84be28a183abb507e125b384122d2c25fae81565b60006104ab7f00000000000000000000000062d5b84be28a183abb507e125b384122d2c25fae8484610669565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020549091505b7f000000000000000000000000000000000000000000000000000000000000000560ff168110156105365773ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604081208054600190810182559152016104d5565b506000610542426105e2565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604081208054929350909160ff841690811061057b57fe5b60009182526020909120600390910201805490915042037f00000000000000000000000000000000000000000000000000000000000000788111156105da576000806105c6866107d3565b504286556001860191909155600285015550505b505050505050565b6000807f0000000000000000000000000000000000000000000000000000000000000078838161060e57fe5b0490507f000000000000000000000000000000000000000000000000000000000000000560ff16818161063d57fe5b069392505050565b7f000000000000000000000000000000000000000000000000000000000000007881565b60008060006106788585610a5a565b604080517fe6a4390500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80851660048301528084166024830152915193955091935088169163e6a4390591604480820192602092909190829003018186803b1580156106f557600080fd5b505afa158015610709573d6000803e3d6000fd5b505050506040513d602081101561071f57600080fd5b50519695505050505050565b600080610737426105e2565b905060007f000000000000000000000000000000000000000000000000000000000000000560ff168260010160ff168161076d57fe5b0690506000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208160ff16815481106107bc57fe5b906000526020600020906003020192505050919050565b60008060006107e0610c20565b90508373ffffffffffffffffffffffffffffffffffffffff16635909c0d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561082857600080fd5b505afa15801561083c573d6000803e3d6000fd5b505050506040513d602081101561085257600080fd5b5051604080517f5a3d5493000000000000000000000000000000000000000000000000000000008152905191945073ffffffffffffffffffffffffffffffffffffffff861691635a3d549391600480820192602092909190829003018186803b1580156108be57600080fd5b505afa1580156108d2573d6000803e3d6000fd5b505050506040513d60208110156108e857600080fd5b5051604080517f0902f1ac00000000000000000000000000000000000000000000000000000000815290519193506000918291829173ffffffffffffffffffffffffffffffffffffffff891691630902f1ac916004808301926060929190829003018186803b15801561095a57600080fd5b505afa15801561096e573d6000803e3d6000fd5b505050506040513d606081101561098457600080fd5b5080516020820151604090920151909450909250905063ffffffff80821690851614610a505780840363ffffffff81166109d16dffffffffffffffffffffffffffff808616908716610c2a565b600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1602870196508063ffffffff16610a29856dffffffffffffffffffffffffffff16856dffffffffffffffffffffffffffff16610c2a565b517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16029590950194505b5050509193909250565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061111c6025913960400191505060405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1610610b1c578284610b1f565b83835b909250905073ffffffffffffffffffffffffffffffffffffffff8216610ba657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b9250929050565b6000610bb7611097565b60405180602001604052808588880381610bcd57fe5b047bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690529050610c02610bfd8285610e3f565b610f02565b71ffffffffffffffffffffffffffffffffffff169695505050505050565b63ffffffff421690565b610c32611097565b60008211610c8b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806111416026913960400191505060405180910390fd5b82610ca55750604080516020810190915260008152610e39565b71ffffffffffffffffffffffffffffffffffff8311610d9057600082607085901b81610ccd57fe5b0490507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811115610d5b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4669786564506f696e743a3a6672616374696f6e3a206f766572666c6f770000604482015290519081900360640190fd5b6040518060200160405280827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16815250915050610e39565b6000610dac846e01000000000000000000000000000085610f09565b90507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811115610d5b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4669786564506f696e743a3a6672616374696f6e3a206f766572666c6f770000604482015290519081900360640190fd5b92915050565b610e476110a9565b6000821580610e8257505082517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682810290838281610e7f57fe5b04145b610eed57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4669786564506f696e743a3a6d756c3a206f766572666c6f7700000000000000604482015290519081900360640190fd5b60408051602081019091529081529392505050565b5160701c90565b6000806000610f188686610fdc565b9150915060008480610f2657fe5b868809905082811115610f3a576001820391505b918290039181610f5857848381610f4d57fe5b049350505050610414565b848210610fc657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f46756c6c4d6174683a2046554c4c4449565f4f564552464c4f57000000000000604482015290519081900360640190fd5b610fd1838387611027565b979650505050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8486099050838502925082810391508281101561101f576001820391505b509250929050565b6000818103821680838161103757fe5b04925080858161104357fe5b04945080816000038161105257fe5b60028581038087028203028087028203028087028203028087028203028087028203028087028203029586029003909402930460010193909302939093010292915050565b60408051602081019091526000815290565b604051806020016040528060008152509056fe536c6964696e6757696e646f774f7261636c653a204d495353494e475f484953544f524943414c5f4f42534552564154494f4e536c6964696e6757696e646f774f7261636c653a20554e45585045435445445f54494d455f454c4150534544556e697377617056324c6962726172793a204944454e544943414c5f4144445245535345534669786564506f696e743a3a6672616374696f6e3a206469766973696f6e206279207a65726fa2646970667358221220488b0fc6ff36f00947d18ab620c6ce82823b17e4475325b9008e26123d8ab00064736f6c63430006060033