ETH Price: $2,942.98 (-6.78%)
Gas: 5 Gwei

Contract

0xafca625321Df8D6A068bDD8F1585d489D2acF11b
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Exchange201378372024-06-21 4:54:4714 days ago1718945687IN
0xafca6253...9D2acF11b
0 ETH0.000690653.73126953
Exchange201378352024-06-21 4:54:2314 days ago1718945663IN
0xafca6253...9D2acF11b
0 ETH0.000580453.51994811
Exchange201376782024-06-21 4:22:3514 days ago1718943755IN
0xafca6253...9D2acF11b
0 ETH0.000545044.00942949
Exchange201236162024-06-19 5:10:4716 days ago1718773847IN
0xafca6253...9D2acF11b
0 ETH0.000776964.71161977
Exchange201235382024-06-19 4:54:5916 days ago1718772899IN
0xafca6253...9D2acF11b
0 ETH0.000850325.15804434
Exchange201114132024-06-17 12:09:4717 days ago1718626187IN
0xafca6253...9D2acF11b
0 ETH0.001064156.45038361
Exchange201114022024-06-17 12:07:3517 days ago1718626055IN
0xafca6253...9D2acF11b
0 ETH0.001041756.39486259
Exchange201113312024-06-17 11:53:2318 days ago1718625203IN
0xafca6253...9D2acF11b
0 ETH0.000898585.3465532
Exchange201113292024-06-17 11:52:5918 days ago1718625179IN
0xafca6253...9D2acF11b
0 ETH0.000954835.15854494
Exchange201112892024-06-17 11:44:5918 days ago1718624699IN
0xafca6253...9D2acF11b
0 ETH0.000786284.67836943
Exchange201112872024-06-17 11:44:3518 days ago1718624675IN
0xafca6253...9D2acF11b
0 ETH0.000803584.7812888
Exchange201112852024-06-17 11:44:1118 days ago1718624651IN
0xafca6253...9D2acF11b
0 ETH0.000649764.67109989
Exchange201112812024-06-17 11:43:2318 days ago1718624603IN
0xafca6253...9D2acF11b
0 ETH0.000776194.61835104
Exchange201112792024-06-17 11:42:5918 days ago1718624579IN
0xafca6253...9D2acF11b
0 ETH0.000719464.28082482
Exchange201112772024-06-17 11:42:3518 days ago1718624555IN
0xafca6253...9D2acF11b
0 ETH0.000861314.65331853
Exchange201094832024-06-17 5:40:3518 days ago1718602835IN
0xafca6253...9D2acF11b
0 ETH0.000628773.74119805
Exchange201094812024-06-17 5:40:1118 days ago1718602811IN
0xafca6253...9D2acF11b
0 ETH0.000633113.76861166
Exchange201094792024-06-17 5:39:4718 days ago1718602787IN
0xafca6253...9D2acF11b
0 ETH0.00062313.70741797
Exchange201094772024-06-17 5:39:2318 days ago1718602763IN
0xafca6253...9D2acF11b
0 ETH0.000701063.78753608
Exchange201068952024-06-16 20:59:2318 days ago1718571563IN
0xafca6253...9D2acF11b
0 ETH0.000806944.80131438
Exchange201068932024-06-16 20:58:5918 days ago1718571539IN
0xafca6253...9D2acF11b
0 ETH0.000925474.99994669
Exchange200980342024-06-15 15:17:4719 days ago1718464667IN
0xafca6253...9D2acF11b
0 ETH0.001556998.41174608
Exchange200980322024-06-15 15:17:2319 days ago1718464643IN
0xafca6253...9D2acF11b
0 ETH0.001562028.59282499
Exchange200903452024-06-14 13:28:2320 days ago1718371703IN
0xafca6253...9D2acF11b
0 ETH0.001607528.6933138
Exchange200903432024-06-14 13:27:5920 days ago1718371679IN
0xafca6253...9D2acF11b
0 ETH0.001761818.7212125
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
194226912024-03-13 0:50:59114 days ago1710291059  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LLAMMA - crvUSD AMM

Compiler Version
vyper:0.3.10

Optimization Enabled:
N/A

Other Settings:
None license

Contract Source Code (Vyper language format)

# @version 0.3.10
"""
@title LLAMMA - crvUSD AMM
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
"""

# Glossary of variables and terms
# =======================
# * ticks, bands - price ranges where liquidity is deposited
# * x - coin which is being borrowed, typically stablecoin
# * y - collateral coin (for example, wETH)
# * A - amplification, the measure of how concentrated the tick is
# * rate - interest rate
# * rate_mul - rate multiplier, 1 + integral(rate * dt)
# * active_band - current band. Other bands are either in one or other coin, but not both
# * min_band - bands below this are definitely empty
# * max_band - bands above this are definitely empty
# * bands_x[n], bands_y[n] - amounts of coin x or y deposited in band n
# * user_shares[user,n] / total_shares[n] - fraction of n'th band owned by a user
# * p_oracle - external oracle price (can be from another AMM)
# * p (as in get_p) - current price of AMM. It depends not only on the balances (x,y) in the band and active_band, but
# also on p_oracle
# * p_current_up, p_current_down - the value of p at constant p_oracle when y=0 or x=0 respectively for the band n
# * p_oracle_up, p_oracle_down - edges of the band when p=p_oracle (steady state), happen when x=0 or y=0 respectively,
# for band n.
# * Grid of bands is set for p_oracle values such as:
#   * p_oracle_up(n) = base_price * ((A - 1) / A)**n
#   * p_oracle_down(n) = p_oracle_up(n) * (A - 1) / A = p_oracle_up(n+1)
# * p_current_up and p_oracle_up change in opposite directions with n
# * When intereste is accrued - all the grid moves by change of base_price
#
# Bonding curve reads as:
# (f + x) * (g + y) = Inv = p_oracle * A**2 * y0**2
# =======================

interface ERC20:
    def transfer(_to: address, _value: uint256) -> bool: nonpayable
    def transferFrom(_from: address, _to: address, _value: uint256) -> bool: nonpayable
    def approve(_spender: address, _value: uint256) -> bool: nonpayable

interface PriceOracle:
    def price() -> uint256: view
    def price_w() -> uint256: nonpayable

interface LMGauge:
    def callback_collateral_shares(n: int256, collateral_per_share: DynArray[uint256, MAX_TICKS_UINT]): nonpayable
    def callback_user_shares(user: address, n: int256, user_shares: DynArray[uint256, MAX_TICKS_UINT]): nonpayable


event TokenExchange:
    buyer: indexed(address)
    sold_id: uint256
    tokens_sold: uint256
    bought_id: uint256
    tokens_bought: uint256

event Deposit:
    provider: indexed(address)
    amount: uint256
    n1: int256
    n2: int256

event Withdraw:
    provider: indexed(address)
    amount_borrowed: uint256
    amount_collateral: uint256

event SetRate:
    rate: uint256
    rate_mul: uint256
    time: uint256

event SetFee:
    fee: uint256

event SetAdminFee:
    fee: uint256


MAX_TICKS: constant(int256) = 50
MAX_TICKS_UINT: constant(uint256) = 50
MAX_SKIP_TICKS: constant(int256) = 1024


struct UserTicks:
    ns: int256  # packs n1 and n2, each is int128
    ticks: uint256[MAX_TICKS/2]  # Share fractions packed 2 per slot

struct DetailedTrade:
    in_amount: uint256
    out_amount: uint256
    n1: int256
    n2: int256
    ticks_in: DynArray[uint256, MAX_TICKS_UINT]
    last_tick_j: uint256
    admin_fee: uint256


BORROWED_TOKEN: immutable(ERC20)    # x
BORROWED_PRECISION: immutable(uint256)
COLLATERAL_TOKEN: immutable(ERC20)  # y
COLLATERAL_PRECISION: immutable(uint256)
BASE_PRICE: immutable(uint256)
admin: public(address)

A: public(immutable(uint256))
Aminus1: immutable(uint256)
A2: immutable(uint256)
Aminus12: immutable(uint256)
SQRT_BAND_RATIO: immutable(uint256)  # sqrt(A / (A - 1))
LOG_A_RATIO: immutable(int256)  # ln(A / (A - 1))
MAX_ORACLE_DN_POW: immutable(uint256)  # (A / (A - 1)) ** 50

fee: public(uint256)
admin_fee: public(uint256)
rate: public(uint256)
rate_time: uint256
rate_mul: uint256
active_band: public(int256)
min_band: public(int256)
max_band: public(int256)

admin_fees_x: public(uint256)
admin_fees_y: public(uint256)

price_oracle_contract: public(immutable(PriceOracle))
old_p_o: uint256
old_dfee: uint256
prev_p_o_time: uint256
PREV_P_O_DELAY: constant(uint256) = 2 * 60  # s = 2 min
MAX_P_O_CHG: constant(uint256) = 12500 * 10**14  # <= 2**(1/3) - max relative change to have fee < 50%

bands_x: public(HashMap[int256, uint256])
bands_y: public(HashMap[int256, uint256])

total_shares: HashMap[int256, uint256]
user_shares: HashMap[address, UserTicks]
DEAD_SHARES: constant(uint256) = 1000

liquidity_mining_callback: public(LMGauge)


@external
def __init__(
        _borrowed_token: address,
        _borrowed_precision: uint256,
        _collateral_token: address,
        _collateral_precision: uint256,
        _A: uint256,
        _sqrt_band_ratio: uint256,
        _log_A_ratio: int256,
        _base_price: uint256,
        fee: uint256,
        admin_fee: uint256,
        _price_oracle_contract: address,
    ):
    """
    @notice LLAMMA constructor
    @param _borrowed_token Token which is being borrowed
    @param _collateral_token Token used as collateral
    @param _collateral_precision Precision of collateral: we pass it because we want the blueprint to fit into bytecode
    @param _A "Amplification coefficient" which also defines density of liquidity and band size. Relative band size is 1/_A
    @param _sqrt_band_ratio Precomputed int(sqrt(A / (A - 1)) * 1e18)
    @param _log_A_ratio Precomputed int(ln(A / (A - 1)) * 1e18)
    @param _base_price Typically the initial crypto price at which AMM is deployed. Will correspond to band 0
    @param fee Relative fee of the AMM: int(fee * 1e18)
    @param admin_fee Admin fee: how much of fee goes to admin. 50% === int(0.5 * 1e18)
    @param _price_oracle_contract External price oracle which has price() and price_w() methods
           which both return current price of collateral multiplied by 1e18
    """
    BORROWED_TOKEN = ERC20(_borrowed_token)
    BORROWED_PRECISION = _borrowed_precision
    COLLATERAL_TOKEN = ERC20(_collateral_token)
    COLLATERAL_PRECISION = _collateral_precision
    A = _A
    BASE_PRICE = _base_price

    Aminus1 = unsafe_sub(A, 1)
    A2 = pow_mod256(A, 2)
    Aminus12 = pow_mod256(unsafe_sub(A, 1), 2)

    self.fee = fee
    self.admin_fee = admin_fee
    price_oracle_contract = PriceOracle(_price_oracle_contract)
    self.prev_p_o_time = block.timestamp
    self.old_p_o = price_oracle_contract.price()

    self.rate_mul = 10**18

    # sqrt(A / (A - 1)) - needs to be pre-calculated externally
    SQRT_BAND_RATIO = _sqrt_band_ratio
    # log(A / (A - 1)) - needs to be pre-calculated externally
    LOG_A_RATIO = _log_A_ratio

    # (A / (A - 1)) ** 50
    # This is not gas-optimal but good with bytecode size and does not overflow
    pow: uint256 = 10**18
    for i in range(50):
        pow = unsafe_div(pow * A, Aminus1)
    MAX_ORACLE_DN_POW = pow


@internal
def approve_max(token: ERC20, _admin: address):
    """
    Approve max in a separate function because it uses less bytespace than
    calling directly, and gas doesn't matter in set_admin
    """
    assert token.approve(_admin, max_value(uint256), default_return_value=True)


@external
def set_admin(_admin: address):
    """
    @notice Set admin of the AMM. Typically it's a controller (unless it's tests)
    @param _admin Admin address
    """
    assert self.admin == empty(address)
    self.admin = _admin
    self.approve_max(BORROWED_TOKEN, _admin)
    self.approve_max(COLLATERAL_TOKEN, _admin)


@internal
@pure
def sqrt_int(_x: uint256) -> uint256:
    """
    @notice Wrapping isqrt builtin because otherwise it will be repeated every time instead of calling
    @param _x Square root's input in "normal" units, e.g. sqrt_int(1) == 1
    """
    return isqrt(_x)


@external
@pure
def coins(i: uint256) -> address:
    return [BORROWED_TOKEN.address, COLLATERAL_TOKEN.address][i]


@internal
@view
def limit_p_o(p: uint256) -> uint256[2]:
    """
    @notice Limits oracle price to avoid losses at abrupt changes, as well as calculates a dynamic fee.
        If we consider oracle_change such as:
            ratio = p_new / p_old
        (let's take for simplicity p_new < p_old, otherwise we compute p_old / p_new)
        Then if the minimal AMM fee will be:
            fee = (1 - ratio**3),
        AMM will not have a loss associated with the price change.
        However, over time fee should still go down (over PREV_P_O_DELAY), and also ratio should be limited
        because we don't want the fee to become too large (say, 50%) which is achieved by limiting the instantaneous
        change in oracle price.

    @return (limited_price_oracle, dynamic_fee)
    """
    p_new: uint256 = p
    dt: uint256 = unsafe_sub(PREV_P_O_DELAY, min(PREV_P_O_DELAY, block.timestamp - self.prev_p_o_time))
    ratio: uint256 = 0

    # ratio = 1 - (p_o_min / p_o_max)**3

    if dt > 0:
        old_p_o: uint256 = self.old_p_o
        old_ratio: uint256 = self.old_dfee
        # ratio = p_o_min / p_o_max
        if p > old_p_o:
            ratio = unsafe_div(old_p_o * 10**18, p)
            if ratio < 10**36 / MAX_P_O_CHG:
                p_new = unsafe_div(old_p_o * MAX_P_O_CHG, 10**18)
                ratio = 10**36 / MAX_P_O_CHG
        else:
            ratio = unsafe_div(p * 10**18, old_p_o)
            if ratio < 10**36 / MAX_P_O_CHG:
                p_new = unsafe_div(old_p_o * 10**18, MAX_P_O_CHG)
                ratio = 10**36 / MAX_P_O_CHG

        # ratio is lower than 1e18
        # Also guaranteed to be limited, therefore can have all ops unsafe
        ratio = min(
            unsafe_div(
                unsafe_mul(
                    unsafe_sub(unsafe_add(10**18, old_ratio), unsafe_div(pow_mod256(ratio, 3), 10**36)),  # (f' + (1 - r**3))
                    dt),                                                                                  # * dt / T
            PREV_P_O_DELAY),
        10**18 - 1)

    return [p_new, ratio]


@internal
@pure
def get_dynamic_fee(p_o: uint256, p_o_up: uint256) -> uint256:
    """
    Dynamic fee equal to a quarter of difference between current price and the price of price oracle
    """
    p_c_d: uint256 = unsafe_div(unsafe_div(p_o ** 2, p_o_up) * p_o, p_o_up)
    p_c_u: uint256 = unsafe_div(unsafe_div(p_c_d * A, Aminus1) * A, Aminus1)
    if p_o < p_c_d:
        return unsafe_div(unsafe_sub(p_c_d, p_o) * (10**18 / 4), p_c_d)
    elif p_o > p_c_u:
        return unsafe_div(unsafe_sub(p_o, p_c_u) * (10**18 / 4), p_o)
    else:
        return 0


@internal
@view
def _price_oracle_ro() -> uint256[2]:
    return self.limit_p_o(price_oracle_contract.price())


@internal
def _price_oracle_w() -> uint256[2]:
    p: uint256[2] = self.limit_p_o(price_oracle_contract.price_w())
    self.prev_p_o_time = block.timestamp
    self.old_p_o = p[0]
    self.old_dfee = p[1]
    return p


@external
@view
def price_oracle() -> uint256:
    """
    @notice Value returned by the external price oracle contract
    """
    return self._price_oracle_ro()[0]


@external
@view
def dynamic_fee() -> uint256:
    """
    @notice Dynamic fee which accounts for price_oracle shifts
    """
    return max(self.fee, self._price_oracle_ro()[1])


@internal
@view
def _rate_mul() -> uint256:
    """
    @notice Rate multiplier which is 1.0 + integral(rate, dt)
    @return Rate multiplier in units where 1.0 == 1e18
    """
    return unsafe_div(self.rate_mul * (10**18 + self.rate * (block.timestamp - self.rate_time)), 10**18)


@external
@view
def get_rate_mul() -> uint256:
    """
    @notice Rate multiplier which is 1.0 + integral(rate, dt)
    @return Rate multiplier in units where 1.0 == 1e18
    """
    return self._rate_mul()


@internal
@view
def _base_price() -> uint256:
    """
    @notice Price which corresponds to band 0.
            Base price grows with time to account for interest rate (which is 0 by default)
    """
    return unsafe_div(BASE_PRICE * self._rate_mul(), 10**18)


@external
@view
def get_base_price() -> uint256:
    """
    @notice Price which corresponds to band 0.
            Base price grows with time to account for interest rate (which is 0 by default)
    """
    return self._base_price()


@internal
@view
def _p_oracle_up(n: int256) -> uint256:
    """
    @notice Upper oracle price for the band to have liquidity when p = p_oracle
    @param n Band number (can be negative)
    @return Price at 1e18 base
    """
    # p_oracle_up(n) = p_base * ((A - 1) / A) ** n
    # p_oracle_down(n) = p_base * ((A - 1) / A) ** (n + 1) = p_oracle_up(n+1)
    # return unsafe_div(self._base_price() * self.exp_int(-n * LOG_A_RATIO), 10**18)

    power: int256 = -n * LOG_A_RATIO

    # ((A - 1) / A) ** n = exp(-n * ln(A / (A - 1))) = exp(-n * LOG_A_RATIO)
    ## Exp implementation based on solmate's
    assert power > -41446531673892821376
    assert power < 135305999368893231589

    x: int256 = unsafe_div(unsafe_mul(power, 2**96), 10**18)

    k: int256 = unsafe_div(
        unsafe_add(
            unsafe_div(unsafe_mul(x, 2**96), 54916777467707473351141471128),
            2**95),
        2**96)
    x = unsafe_sub(x, unsafe_mul(k, 54916777467707473351141471128))

    y: int256 = unsafe_add(x, 1346386616545796478920950773328)
    y = unsafe_add(unsafe_div(unsafe_mul(y, x), 2**96), 57155421227552351082224309758442)
    p: int256 = unsafe_sub(unsafe_add(y, x), 94201549194550492254356042504812)
    p = unsafe_add(unsafe_div(unsafe_mul(p, y), 2**96), 28719021644029726153956944680412240)
    p = unsafe_add(unsafe_mul(p, x), (4385272521454847904659076985693276 * 2**96))

    q: int256 = x - 2855989394907223263936484059900
    q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 50020603652535783019961831881945)
    q = unsafe_sub(unsafe_div(unsafe_mul(q, x), 2**96), 533845033583426703283633433725380)
    q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 3604857256930695427073651918091429)
    q = unsafe_sub(unsafe_div(unsafe_mul(q, x), 2**96), 14423608567350463180887372962807573)
    q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 26449188498355588339934803723976023)

    exp_result: uint256 = shift(
        unsafe_mul(convert(unsafe_div(p, q), uint256), 3822833074963236453042738258902158003155416615667),
        unsafe_sub(k, 195))
    ## End exp
    assert exp_result > 1000  # dev: limit precision of the multiplier
    return unsafe_div(self._base_price() * exp_result, 10**18)


@internal
@view
def _p_current_band(n: int256) -> uint256:
    """
    @notice Lowest possible price of the band at current oracle price
    @param n Band number (can be negative)
    @return Price at 1e18 base
    """
    # k = (self.A - 1) / self.A  # equal to (p_down / p_up)
    # p_base = self.p_base * k ** n = p_oracle_up(n)
    p_base: uint256 = self._p_oracle_up(n)

    # return self.p_oracle**3 / p_base**2
    p_oracle: uint256 = self._price_oracle_ro()[0]
    return unsafe_div(p_oracle**2 / p_base * p_oracle, p_base)


@external
@view
def p_current_up(n: int256) -> uint256:
    """
    @notice Highest possible price of the band at current oracle price
    @param n Band number (can be negative)
    @return Price at 1e18 base
    """
    return self._p_current_band(n + 1)


@external
@view
def p_current_down(n: int256) -> uint256:
    """
    @notice Lowest possible price of the band at current oracle price
    @param n Band number (can be negative)
    @return Price at 1e18 base
    """
    return self._p_current_band(n)


@external
@view
def p_oracle_up(n: int256) -> uint256:
    """
    @notice Highest oracle price for the band to have liquidity when p = p_oracle
    @param n Band number (can be negative)
    @return Price at 1e18 base
    """
    return self._p_oracle_up(n)


@external
@view
def p_oracle_down(n: int256) -> uint256:
    """
    @notice Lowest oracle price for the band to have liquidity when p = p_oracle
    @param n Band number (can be negative)
    @return Price at 1e18 base
    """
    return self._p_oracle_up(n + 1)


@internal
@pure
def _get_y0(x: uint256, y: uint256, p_o: uint256, p_o_up: uint256) -> uint256:
    """
    @notice Calculate y0 for the invariant based on current liquidity in band.
            The value of y0 has a meaning of amount of collateral when band has no stablecoin
            but current price is equal to both oracle price and upper band price.
    @param x Amount of stablecoin in band
    @param y Amount of collateral in band
    @param p_o External oracle price
    @param p_o_up Upper boundary of the band
    @return y0
    """
    assert p_o != 0
    # solve:
    # p_o * A * y0**2 - y0 * (p_oracle_up/p_o * (A-1) * x + p_o**2/p_oracle_up * A * y) - xy = 0
    b: uint256 = 0
    # p_o_up * unsafe_sub(A, 1) * x / p_o + A * p_o**2 / p_o_up * y / 10**18
    if x != 0:
        b = unsafe_div(p_o_up * Aminus1 * x, p_o)
    if y != 0:
        b += unsafe_div(A * p_o**2 / p_o_up * y, 10**18)
    if x > 0 and y > 0:
        D: uint256 = b**2 + unsafe_div((unsafe_mul(4, A) * p_o) * y, 10**18) * x
        return unsafe_div((b + self.sqrt_int(D)) * 10**18, unsafe_mul(unsafe_mul(2, A), p_o))
    else:
        return unsafe_div(b * 10**18, unsafe_mul(A, p_o))


@internal
@view
def _get_p(n: int256, x: uint256, y: uint256) -> uint256:
    """
    @notice Get current AMM price in band
    @param n Band number
    @param x Amount of stablecoin in band
    @param y Amount of collateral in band
    @return Current price at 1e18 base
    """
    p_o_up: uint256 = self._p_oracle_up(n)
    p_o: uint256 = self._price_oracle_ro()[0]
    assert p_o_up != 0

    # Special cases
    if x == 0:
        if y == 0:  # x and y are 0
            # Return mid-band
            return unsafe_div((unsafe_div(unsafe_div(p_o**2, p_o_up) * p_o, p_o_up) * A), Aminus1)
        # if x == 0: # Lowest point of this band -> p_current_down
        return unsafe_div(unsafe_div(p_o**2, p_o_up) * p_o, p_o_up)
    if y == 0: # Highest point of this band -> p_current_up
        p_o_up = unsafe_div(p_o_up * Aminus1, A)  # now this is _actually_ p_o_down
        return unsafe_div(p_o**2 / p_o_up * p_o, p_o_up)

    y0: uint256 = self._get_y0(x, y, p_o, p_o_up)
    # ^ that call also checks that p_o != 0

    # (f(y0) + x) / (g(y0) + y)
    f: uint256 = unsafe_div(A * y0 * p_o, p_o_up) * p_o
    g: uint256 = unsafe_div(Aminus1 * y0 * p_o_up, p_o)
    return (f + x * 10**18) / (g + y)


@external
@view
@nonreentrant('lock')
def get_p() -> uint256:
    """
    @notice Get current AMM price in active_band
    @return Current price at 1e18 base
    """
    n: int256 = self.active_band
    return self._get_p(n, self.bands_x[n], self.bands_y[n])


@internal
@view
def _read_user_tick_numbers(user: address) -> int256[2]:
    """
    @notice Unpacks and reads user tick numbers
    @param user User address
    @return Lowest and highest band the user deposited into
    """
    ns: int256 = self.user_shares[user].ns
    n2: int256 = unsafe_div(ns, 2**128)
    n1: int256 = ns % 2**128
    if n1 >= 2**127:
        n1 = unsafe_sub(n1, 2**128)
        n2 = unsafe_add(n2, 1)
    return [n1, n2]


@external
@view
@nonreentrant('lock')
def read_user_tick_numbers(user: address) -> int256[2]:
    """
    @notice Unpacks and reads user tick numbers
    @param user User address
    @return Lowest and highest band the user deposited into
    """
    return self._read_user_tick_numbers(user)


@internal
@view
def _read_user_ticks(user: address, ns: int256[2]) -> DynArray[uint256, MAX_TICKS_UINT]:
    """
    @notice Unpacks and reads user ticks (shares) for all the ticks user deposited into
    @param user User address
    @param size Number of ticks the user deposited into
    @return Array of shares the user has
    """
    ticks: DynArray[uint256, MAX_TICKS_UINT] = []
    size: uint256 = convert(ns[1] - ns[0] + 1, uint256)
    for i in range(MAX_TICKS / 2):
        if len(ticks) == size:
            break
        tick: uint256 = self.user_shares[user].ticks[i]
        ticks.append(tick & (2**128 - 1))
        if len(ticks) == size:
            break
        ticks.append(shift(tick, -128))
    return ticks


@external
@view
@nonreentrant('lock')
def can_skip_bands(n_end: int256) -> bool:
    """
    @notice Check that we have no liquidity between active_band and `n_end`
    """
    n: int256 = self.active_band
    for i in range(MAX_SKIP_TICKS):
        if n_end > n:
            if self.bands_y[n] != 0:
                return False
            n = unsafe_add(n, 1)
        else:
            if self.bands_x[n] != 0:
                return False
            n = unsafe_sub(n, 1)
        if n == n_end:  # not including n_end
            break
    return True
    # Actually skipping bands:
    # * change self.active_band to the new n
    # * change self.p_base_mul
    # to do n2-n1 times (if n2 > n1):
    # out.base_mul = unsafe_div(out.base_mul * Aminus1, A)


@external
@view
@nonreentrant('lock')
def active_band_with_skip() -> int256:
    n0: int256 = self.active_band
    n: int256 = n0
    min_band: int256 = self.min_band
    for i in range(MAX_SKIP_TICKS):
        if n < min_band:
            n = n0 - MAX_SKIP_TICKS
            break
        if self.bands_x[n] != 0:
            break
        n -= 1
    return n


@external
@view
@nonreentrant('lock')
def has_liquidity(user: address) -> bool:
    """
    @notice Check if `user` has any liquidity in the AMM
    """
    return self.user_shares[user].ticks[0] != 0


@internal
def save_user_shares(user: address, user_shares: DynArray[uint256, MAX_TICKS_UINT]):
    ptr: uint256 = 0
    for j in range(MAX_TICKS_UINT / 2):
        if ptr >= len(user_shares):
            break
        tick: uint256 = user_shares[ptr]
        ptr = unsafe_add(ptr, 1)
        if len(user_shares) != ptr:
            tick = tick | shift(user_shares[ptr], 128)
        ptr = unsafe_add(ptr, 1)
        self.user_shares[user].ticks[j] = tick


@external
@nonreentrant('lock')
def deposit_range(user: address, amount: uint256, n1: int256, n2: int256):
    """
    @notice Deposit for a user in a range of bands. Only admin contract (Controller) can do it
    @param user User address
    @param amount Amount of collateral to deposit
    @param n1 Lower band in the deposit range
    @param n2 Upper band in the deposit range
    """
    assert msg.sender == self.admin

    user_shares: DynArray[uint256, MAX_TICKS_UINT] = []
    collateral_shares: DynArray[uint256, MAX_TICKS_UINT] = []

    n0: int256 = self.active_band

    # We assume that n1,n2 area already sorted (and they are in Controller)
    assert n2 < 2**127
    assert n1 > -2**127

    n_bands: uint256 = unsafe_add(convert(unsafe_sub(n2, n1), uint256), 1)
    assert n_bands <= MAX_TICKS_UINT

    y_per_band: uint256 = unsafe_div(amount * COLLATERAL_PRECISION, n_bands)
    assert y_per_band > 100, "Amount too low"

    assert self.user_shares[user].ticks[0] == 0  # dev: User must have no liquidity
    self.user_shares[user].ns = unsafe_add(n1, unsafe_mul(n2, 2**128))

    lm: LMGauge = self.liquidity_mining_callback

    # Autoskip bands if we can
    for i in range(MAX_SKIP_TICKS + 1):
        if n1 > n0:
            if i != 0:
                self.active_band = n0
            break
        assert self.bands_x[n0] == 0 and i < MAX_SKIP_TICKS, "Deposit below current band"
        n0 -= 1

    for i in range(MAX_TICKS):
        band: int256 = unsafe_add(n1, i)
        if band > n2:
            break

        assert self.bands_x[band] == 0, "Band not empty"
        y: uint256 = y_per_band
        if i == 0:
            y = amount * COLLATERAL_PRECISION - y * unsafe_sub(n_bands, 1)

        total_y: uint256 = self.bands_y[band]

        # Total / user share
        s: uint256 = self.total_shares[band]
        ds: uint256 = unsafe_div((s + DEAD_SHARES) * y, total_y + 1)
        assert ds > 0, "Amount too low"
        user_shares.append(ds)
        s += ds
        assert s <= 2**128 - 1
        self.total_shares[band] = s

        total_y += y
        self.bands_y[band] = total_y

        if lm.address != empty(address):
            # If initial s == 0 - s becomes equal to y which is > 100 => nonzero
            collateral_shares.append(unsafe_div(total_y * 10**18, s))

    self.min_band = min(self.min_band, n1)
    self.max_band = max(self.max_band, n2)

    self.save_user_shares(user, user_shares)

    log Deposit(user, amount, n1, n2)

    if lm.address != empty(address):
        lm.callback_collateral_shares(n1, collateral_shares)
        lm.callback_user_shares(user, n1, user_shares)


@external
@nonreentrant('lock')
def withdraw(user: address, frac: uint256) -> uint256[2]:
    """
    @notice Withdraw liquidity for the user. Only admin contract can do it
    @param user User who owns liquidity
    @param frac Fraction to withdraw (1e18 being 100%)
    @return Amount of [stablecoins, collateral] withdrawn
    """
    assert msg.sender == self.admin
    assert frac <= 10**18

    lm: LMGauge = self.liquidity_mining_callback

    ns: int256[2] = self._read_user_tick_numbers(user)
    n: int256 = ns[0]
    user_shares: DynArray[uint256, MAX_TICKS_UINT] = self._read_user_ticks(user, ns)
    assert user_shares[0] > 0, "No deposits"

    total_x: uint256 = 0
    total_y: uint256 = 0
    min_band: int256 = self.min_band
    old_min_band: int256 = min_band
    old_max_band: int256 = self.max_band
    max_band: int256 = n - 1

    for i in range(MAX_TICKS):
        x: uint256 = self.bands_x[n]
        y: uint256 = self.bands_y[n]
        ds: uint256 = unsafe_div(frac * user_shares[i], 10**18)
        user_shares[i] = unsafe_sub(user_shares[i], ds)  # Can ONLY zero out when frac == 10**18
        s: uint256 = self.total_shares[n]
        new_shares: uint256 = s - ds
        self.total_shares[n] = new_shares
        s += DEAD_SHARES  # after this s is guaranteed to be bigger than 0
        dx: uint256 = unsafe_div((x + 1) * ds, s)
        dy: uint256 = unsafe_div((y + 1) * ds, s)

        x -= dx
        y -= dy

        # If withdrawal is the last one - transfer dust to admin fees
        if new_shares == 0:
            if x > 0:
                self.admin_fees_x += unsafe_div(x, BORROWED_PRECISION)
            if y > 0:
                self.admin_fees_y += unsafe_div(y, COLLATERAL_PRECISION)
            x = 0
            y = 0

        if n == min_band:
            if x == 0:
                if y == 0:
                    min_band += 1
        if x > 0 or y > 0:
            max_band = n
        self.bands_x[n] = x
        self.bands_y[n] = y
        total_x += dx
        total_y += dy

        if n == ns[1]:
            break
        else:
            n = unsafe_add(n, 1)

    # Empty the ticks
    if frac == 10**18:
        self.user_shares[user].ticks[0] = 0
    else:
        self.save_user_shares(user, user_shares)

    if old_min_band != min_band:
        self.min_band = min_band
    if old_max_band <= ns[1]:
        self.max_band = max_band

    total_x = unsafe_div(total_x, BORROWED_PRECISION)
    total_y = unsafe_div(total_y, COLLATERAL_PRECISION)
    log Withdraw(user, total_x, total_y)

    if lm.address != empty(address):
        lm.callback_collateral_shares(0, [])  # collateral/shares ratio is unchanged
        lm.callback_user_shares(user, ns[0], user_shares)

    return [total_x, total_y]


@internal
@view
def calc_swap_out(pump: bool, in_amount: uint256, p_o: uint256[2], in_precision: uint256, out_precision: uint256) -> DetailedTrade:
    """
    @notice Calculate the amount which can be obtained as a result of exchange.
            If couldn't exchange all - will also update the amount which was actually used.
            Also returns other parameters related to state after swap.
            This function is core to the AMM functionality.
    @param pump Indicates whether the trade buys or sells collateral
    @param in_amount Amount of token going in
    @param p_o Current oracle price and ratio (p_o, dynamic_fee)
    @return Amounts spent and given out, initial and final bands of the AMM, new
            amounts of coins in bands in the AMM, as well as admin fee charged,
            all in one data structure
    """
    # pump = True: borrowable (USD) in, collateral (ETH) out; going up
    # pump = False: collateral (ETH) in, borrowable (USD) out; going down
    min_band: int256 = self.min_band
    max_band: int256 = self.max_band
    out: DetailedTrade = empty(DetailedTrade)
    out.n2 = self.active_band
    p_o_up: uint256 = self._p_oracle_up(out.n2)
    x: uint256 = self.bands_x[out.n2]
    y: uint256 = self.bands_y[out.n2]

    in_amount_left: uint256 = in_amount
    fee: uint256 = max(self.fee, p_o[1])
    admin_fee: uint256 = self.admin_fee
    j: uint256 = MAX_TICKS_UINT

    for i in range(MAX_TICKS + MAX_SKIP_TICKS):
        y0: uint256 = 0
        f: uint256 = 0
        g: uint256 = 0
        Inv: uint256 = 0
        dynamic_fee: uint256 = fee

        if x > 0 or y > 0:
            if j == MAX_TICKS_UINT:
                out.n1 = out.n2
                j = 0
            y0 = self._get_y0(x, y, p_o[0], p_o_up)  # <- also checks p_o
            f = unsafe_div(A * y0 * p_o[0] / p_o_up * p_o[0], 10**18)
            g = unsafe_div(Aminus1 * y0 * p_o_up, p_o[0])
            Inv = (f + x) * (g + y)
            dynamic_fee = max(self.get_dynamic_fee(p_o[0], p_o_up), fee)

        antifee: uint256 = unsafe_div(
            (10**18)**2,
            unsafe_sub(10**18, min(dynamic_fee, 10**18 - 1))
        )

        if j != MAX_TICKS_UINT:
            # Initialize
            _tick: uint256 = y
            if pump:
                _tick = x
            out.ticks_in.append(_tick)

        # Need this to break if price is too far
        p_ratio: uint256 = unsafe_div(p_o_up * 10**18, p_o[0])

        if pump:
            if y != 0:
                if g != 0:
                    x_dest: uint256 = (unsafe_div(Inv, g) - f) - x
                    dx: uint256 = unsafe_div(x_dest * antifee, 10**18)
                    if dx >= in_amount_left:
                        # This is the last band
                        x_dest = unsafe_div(in_amount_left * 10**18, antifee)  # LESS than in_amount_left
                        out.last_tick_j = min(Inv / (f + (x + x_dest)) - g + 1, y)  # Should be always >= 0
                        x_dest = unsafe_div(unsafe_sub(in_amount_left, x_dest) * admin_fee, 10**18)  # abs admin fee now
                        x += in_amount_left  # x is precise after this
                        # Round down the output
                        out.out_amount += y - out.last_tick_j
                        out.ticks_in[j] = x - x_dest
                        out.in_amount = in_amount
                        out.admin_fee = unsafe_add(out.admin_fee, x_dest)
                        break

                    else:
                        # We go into the next band
                        dx = max(dx, 1)  # Prevents from leaving dust in the band
                        x_dest = unsafe_div(unsafe_sub(dx, x_dest) * admin_fee, 10**18)  # abs admin fee now
                        in_amount_left -= dx
                        out.ticks_in[j] = x + dx - x_dest
                        out.in_amount += dx
                        out.out_amount += y
                        out.admin_fee = unsafe_add(out.admin_fee, x_dest)

            if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
                if out.n2 == max_band:
                    break
                if j == MAX_TICKS_UINT - 1:
                    break
                if p_ratio < unsafe_div(10**36, MAX_ORACLE_DN_POW):
                    # Don't allow to be away by more than ~50 ticks
                    break
                out.n2 += 1
                p_o_up = unsafe_div(p_o_up * Aminus1, A)
                x = 0
                y = self.bands_y[out.n2]

        else:  # dump
            if x != 0:
                if f != 0:
                    y_dest: uint256 = (unsafe_div(Inv, f) - g) - y
                    dy: uint256 = unsafe_div(y_dest * antifee, 10**18)
                    if dy >= in_amount_left:
                        # This is the last band
                        y_dest = unsafe_div(in_amount_left * 10**18, antifee)
                        out.last_tick_j = min(Inv / (g + (y + y_dest)) - f + 1, x)
                        y_dest = unsafe_div(unsafe_sub(in_amount_left, y_dest) * admin_fee, 10**18)  # abs admin fee now
                        y += in_amount_left
                        out.out_amount += x - out.last_tick_j
                        out.ticks_in[j] = y - y_dest
                        out.in_amount = in_amount
                        out.admin_fee = unsafe_add(out.admin_fee, y_dest)
                        break

                    else:
                        # We go into the next band
                        dy = max(dy, 1)  # Prevents from leaving dust in the band
                        y_dest = unsafe_div(unsafe_sub(dy, y_dest) * admin_fee, 10**18)  # abs admin fee now
                        in_amount_left -= dy
                        out.ticks_in[j] = y + dy - y_dest
                        out.in_amount += dy
                        out.out_amount += x
                        out.admin_fee = unsafe_add(out.admin_fee, y_dest)

            if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
                if out.n2 == min_band:
                    break
                if j == MAX_TICKS_UINT - 1:
                    break
                if p_ratio > MAX_ORACLE_DN_POW:
                    # Don't allow to be away by more than ~50 ticks
                    break
                out.n2 -= 1
                p_o_up = unsafe_div(p_o_up * A, Aminus1)
                x = self.bands_x[out.n2]
                y = 0

        if j != MAX_TICKS_UINT:
            j = unsafe_add(j, 1)

    # Round up what goes in and down what goes out
    # ceil(in_amount_used/BORROWED_PRECISION) * BORROWED_PRECISION
    out.in_amount = unsafe_mul(unsafe_div(unsafe_add(out.in_amount, unsafe_sub(in_precision, 1)), in_precision), in_precision)
    out.out_amount = unsafe_mul(unsafe_div(out.out_amount, out_precision), out_precision)

    return out


@internal
@view
def _get_dxdy(i: uint256, j: uint256, amount: uint256, is_in: bool) -> DetailedTrade:
    """
    @notice Method to use to calculate out amount and spent in amount
    @param i Input coin index
    @param j Output coin index
    @param amount Amount of input or output coin to swap
    @param is_in Whether IN our OUT amount is known
    @return DetailedTrade with all swap results
    """
    # i = 0: borrowable (USD) in, collateral (ETH) out; going up
    # i = 1: collateral (ETH) in, borrowable (USD) out; going down
    assert (i == 0 and j == 1) or (i == 1 and j == 0), "Wrong index"
    out: DetailedTrade = empty(DetailedTrade)
    if amount == 0:
        return out
    in_precision: uint256 = COLLATERAL_PRECISION
    out_precision: uint256 = BORROWED_PRECISION
    if i == 0:
        in_precision = BORROWED_PRECISION
        out_precision = COLLATERAL_PRECISION
    p_o: uint256[2] = self._price_oracle_ro()
    if is_in:
        out = self.calc_swap_out(i == 0, amount * in_precision, p_o, in_precision, out_precision)
    else:
        out = self.calc_swap_in(i == 0, amount * out_precision, p_o, in_precision, out_precision)
    out.in_amount = unsafe_div(out.in_amount, in_precision)
    out.out_amount = unsafe_div(out.out_amount, out_precision)
    return out


@external
@view
@nonreentrant('lock')
def get_dy(i: uint256, j: uint256, in_amount: uint256) -> uint256:
    """
    @notice Method to use to calculate out amount
    @param i Input coin index
    @param j Output coin index
    @param in_amount Amount of input coin to swap
    @return Amount of coin j to give out
    """
    return self._get_dxdy(i, j, in_amount, True).out_amount


@external
@view
@nonreentrant('lock')
def get_dxdy(i: uint256, j: uint256, in_amount: uint256) -> (uint256, uint256):
    """
    @notice Method to use to calculate out amount and spent in amount
    @param i Input coin index
    @param j Output coin index
    @param in_amount Amount of input coin to swap
    @return A tuple with in_amount used and out_amount returned
    """
    out: DetailedTrade = self._get_dxdy(i, j, in_amount, True)
    return (out.in_amount, out.out_amount)


@internal
def _exchange(i: uint256, j: uint256, amount: uint256, minmax_amount: uint256, _for: address, use_in_amount: bool) -> uint256[2]:
    """
    @notice Exchanges two coins, callable by anyone
    @param i Input coin index
    @param j Output coin index
    @param amount Amount of input/output coin to swap
    @param minmax_amount Minimal/maximum amount to get as output/input
    @param _for Address to send coins to
    @param use_in_amount Whether input or output amount is specified
    @return Amount of coins given in and out
    """
    assert (i == 0 and j == 1) or (i == 1 and j == 0), "Wrong index"
    p_o: uint256[2] = self._price_oracle_w()  # Let's update the oracle even if we exchange 0
    if amount == 0:
        return [0, 0]

    lm: LMGauge = self.liquidity_mining_callback
    collateral_shares: DynArray[uint256, MAX_TICKS_UINT] = []

    in_coin: ERC20 = BORROWED_TOKEN
    out_coin: ERC20 = COLLATERAL_TOKEN
    in_precision: uint256 = BORROWED_PRECISION
    out_precision: uint256 = COLLATERAL_PRECISION
    if i == 1:
        in_precision = out_precision
        in_coin = out_coin
        out_precision = BORROWED_PRECISION
        out_coin = BORROWED_TOKEN

    out: DetailedTrade = empty(DetailedTrade)
    if use_in_amount:
        out = self.calc_swap_out(i == 0, amount * in_precision, p_o, in_precision, out_precision)
    else:
        amount_to_swap: uint256 = max_value(uint256)
        if amount < amount_to_swap:
            amount_to_swap = amount * out_precision
        out = self.calc_swap_in(i == 0, amount_to_swap, p_o, in_precision, out_precision)
    in_amount_done: uint256 = unsafe_div(out.in_amount, in_precision)
    out_amount_done: uint256 = unsafe_div(out.out_amount, out_precision)
    if use_in_amount:
        assert out_amount_done >= minmax_amount, "Slippage"
    else:
        assert in_amount_done <= minmax_amount and (out_amount_done == amount or amount == max_value(uint256)), "Slippage"
    if out_amount_done == 0 or in_amount_done == 0:
        return [0, 0]

    out.admin_fee = unsafe_div(out.admin_fee, in_precision)
    if i == 0:
        self.admin_fees_x += out.admin_fee
    else:
        self.admin_fees_y += out.admin_fee

    n: int256 = min(out.n1, out.n2)
    n_start: int256 = n
    n_diff: int256 = abs(unsafe_sub(out.n2, out.n1))

    for k in range(MAX_TICKS):
        x: uint256 = 0
        y: uint256 = 0
        if i == 0:
            x = out.ticks_in[k]
            if n == out.n2:
                y = out.last_tick_j
        else:
            y = out.ticks_in[unsafe_sub(n_diff, k)]
            if n == out.n2:
                x = out.last_tick_j
        self.bands_x[n] = x
        self.bands_y[n] = y
        if lm.address != empty(address):
            s: uint256 = 0
            if y > 0:
                s = unsafe_div(y * 10**18, self.total_shares[n])
            collateral_shares.append(s)
        if k == n_diff:
            break
        n = unsafe_add(n, 1)

    self.active_band = out.n2

    log TokenExchange(_for, i, in_amount_done, j, out_amount_done)

    if lm.address != empty(address):
        lm.callback_collateral_shares(n_start, collateral_shares)

    assert in_coin.transferFrom(msg.sender, self, in_amount_done, default_return_value=True)
    assert out_coin.transfer(_for, out_amount_done, default_return_value=True)

    return [in_amount_done, out_amount_done]


@internal
@view
def calc_swap_in(pump: bool, out_amount: uint256, p_o: uint256[2], in_precision: uint256, out_precision: uint256) -> DetailedTrade:
    """
    @notice Calculate the input amount required to receive the desired output amount.
            If couldn't exchange all - will also update the amount which was actually received.
            Also returns other parameters related to state after swap.
    @param pump Indicates whether the trade buys or sells collateral
    @param out_amount Desired amount of token going out
    @param p_o Current oracle price and antisandwich fee (p_o, dynamic_fee)
    @return Amounts required and given out, initial and final bands of the AMM, new
            amounts of coins in bands in the AMM, as well as admin fee charged,
            all in one data structure
    """
    # pump = True: borrowable (USD) in, collateral (ETH) out; going up
    # pump = False: collateral (ETH) in, borrowable (USD) out; going down
    min_band: int256 = self.min_band
    max_band: int256 = self.max_band
    out: DetailedTrade = empty(DetailedTrade)
    out.n2 = self.active_band
    p_o_up: uint256 = self._p_oracle_up(out.n2)
    x: uint256 = self.bands_x[out.n2]
    y: uint256 = self.bands_y[out.n2]

    out_amount_left: uint256 = out_amount
    fee: uint256 = max(self.fee, p_o[1])
    admin_fee: uint256 = self.admin_fee
    j: uint256 = MAX_TICKS_UINT

    for i in range(MAX_TICKS + MAX_SKIP_TICKS):
        y0: uint256 = 0
        f: uint256 = 0
        g: uint256 = 0
        Inv: uint256 = 0
        dynamic_fee: uint256 = fee

        if x > 0 or y > 0:
            if j == MAX_TICKS_UINT:
                out.n1 = out.n2
                j = 0
            y0 = self._get_y0(x, y, p_o[0], p_o_up)  # <- also checks p_o
            f = unsafe_div(A * y0 * p_o[0] / p_o_up * p_o[0], 10**18)
            g = unsafe_div(Aminus1 * y0 * p_o_up, p_o[0])
            Inv = (f + x) * (g + y)
            dynamic_fee = max(self.get_dynamic_fee(p_o[0], p_o_up), fee)

        antifee: uint256 = unsafe_div(
            (10**18)**2,
            unsafe_sub(10**18, min(dynamic_fee, 10**18 - 1))
        )

        if j != MAX_TICKS_UINT:
            # Initialize
            _tick: uint256 = y
            if pump:
                _tick = x
            out.ticks_in.append(_tick)

        # Need this to break if price is too far
        p_ratio: uint256 = unsafe_div(p_o_up * 10**18, p_o[0])

        if pump:
            if y != 0:
                if g != 0:
                    if y >= out_amount_left:
                        # This is the last band
                        out.last_tick_j = unsafe_sub(y, out_amount_left)
                        x_dest: uint256 = Inv / (g + out.last_tick_j) - f - x
                        dx: uint256 = unsafe_div(x_dest * antifee, 10**18)  # MORE than x_dest
                        out.out_amount = out_amount  # We successfully found liquidity for all the out_amount
                        out.in_amount += dx
                        x_dest = unsafe_div(unsafe_sub(dx, x_dest) * admin_fee, 10**18)  # abs admin fee now
                        out.ticks_in[j] = x + dx - x_dest
                        out.admin_fee = unsafe_add(out.admin_fee, x_dest)
                        break

                    else:
                        # We go into the next band
                        x_dest: uint256 = (unsafe_div(Inv, g) - f) - x
                        dx: uint256 = max(unsafe_div(x_dest * antifee, 10**18), 1)
                        out_amount_left -= y
                        out.in_amount += dx
                        out.out_amount += y
                        x_dest = unsafe_div(unsafe_sub(dx, x_dest) * admin_fee, 10**18)  # abs admin fee now
                        out.ticks_in[j] = x + dx - x_dest
                        out.admin_fee = unsafe_add(out.admin_fee, x_dest)

            if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
                if out.n2 == max_band:
                    break
                if j == MAX_TICKS_UINT - 1:
                    break
                if p_ratio < unsafe_div(10**36, MAX_ORACLE_DN_POW):
                    # Don't allow to be away by more than ~50 ticks
                    break
                out.n2 += 1
                p_o_up = unsafe_div(p_o_up * Aminus1, A)
                x = 0
                y = self.bands_y[out.n2]

        else:  # dump
            if x != 0:
                if f != 0:
                    if x >= out_amount_left:
                        # This is the last band
                        out.last_tick_j = unsafe_sub(x, out_amount_left)
                        y_dest: uint256 = Inv / (f + out.last_tick_j) - g - y
                        dy: uint256 = unsafe_div(y_dest * antifee, 10**18)  # MORE than y_dest
                        out.out_amount = out_amount
                        out.in_amount += dy
                        y_dest = unsafe_div(unsafe_sub(dy, y_dest) * admin_fee, 10**18)  # abs admin fee now
                        out.ticks_in[j] = y + dy - y_dest
                        out.admin_fee = unsafe_add(out.admin_fee, y_dest)
                        break

                    else:
                        # We go into the next band
                        y_dest: uint256 = (unsafe_div(Inv, f) - g) - y
                        dy: uint256 = max(unsafe_div(y_dest * antifee, 10**18), 1)
                        out_amount_left -= x
                        out.in_amount += dy
                        out.out_amount += x
                        y_dest = unsafe_div(unsafe_sub(dy, y_dest) * admin_fee, 10**18)  # abs admin fee now
                        out.ticks_in[j] = y + dy - y_dest
                        out.admin_fee = unsafe_add(out.admin_fee, y_dest)

            if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
                if out.n2 == min_band:
                    break
                if j == MAX_TICKS_UINT - 1:
                    break
                if p_ratio > MAX_ORACLE_DN_POW:
                    # Don't allow to be away by more than ~50 ticks
                    break
                out.n2 -= 1
                p_o_up = unsafe_div(p_o_up * A, Aminus1)
                x = self.bands_x[out.n2]
                y = 0

        if j != MAX_TICKS_UINT:
            j = unsafe_add(j, 1)

    # Round up what goes in and down what goes out
    # ceil(in_amount_used/BORROWED_PRECISION) * BORROWED_PRECISION
    out.in_amount = unsafe_mul(unsafe_div(unsafe_add(out.in_amount, unsafe_sub(in_precision, 1)), in_precision), in_precision)
    out.out_amount = unsafe_mul(unsafe_div(out.out_amount, out_precision), out_precision)

    return out


@external
@view
@nonreentrant('lock')
def get_dx(i: uint256, j: uint256, out_amount: uint256) -> uint256:
    """
    @notice Method to use to calculate in amount required to receive the desired out_amount
    @param i Input coin index
    @param j Output coin index
    @param out_amount Desired amount of output coin to receive
    @return Amount of coin i to spend
    """
    # i = 0: borrowable (USD) in, collateral (ETH) out; going up
    # i = 1: collateral (ETH) in, borrowable (USD) out; going down
    trade: DetailedTrade = self._get_dxdy(i, j, out_amount, False)
    assert trade.out_amount == out_amount
    return trade.in_amount


@external
@view
@nonreentrant('lock')
def get_dydx(i: uint256, j: uint256, out_amount: uint256) -> (uint256, uint256):
    """
    @notice Method to use to calculate in amount required and out amount received
    @param i Input coin index
    @param j Output coin index
    @param out_amount Desired amount of output coin to receive
    @return A tuple with out_amount received and in_amount returned
    """
    # i = 0: borrowable (USD) in, collateral (ETH) out; going up
    # i = 1: collateral (ETH) in, borrowable (USD) out; going down
    out: DetailedTrade = self._get_dxdy(i, j, out_amount, False)
    return (out.out_amount, out.in_amount)


@external
@nonreentrant('lock')
def exchange(i: uint256, j: uint256, in_amount: uint256, min_amount: uint256, _for: address = msg.sender) -> uint256[2]:
    """
    @notice Exchanges two coins, callable by anyone
    @param i Input coin index
    @param j Output coin index
    @param in_amount Amount of input coin to swap
    @param min_amount Minimal amount to get as output
    @param _for Address to send coins to
    @return Amount of coins given in/out
    """
    return self._exchange(i, j, in_amount, min_amount, _for, True)


@external
@nonreentrant('lock')
def exchange_dy(i: uint256, j: uint256, out_amount: uint256, max_amount: uint256, _for: address = msg.sender) -> uint256[2]:
    """
    @notice Exchanges two coins, callable by anyone
    @param i Input coin index
    @param j Output coin index
    @param out_amount Desired amount of output coin to receive
    @param max_amount Maximum amount to spend (revert if more)
    @param _for Address to send coins to
    @return Amount of coins given in/out
    """
    return self._exchange(i, j, out_amount, max_amount, _for, False)


@internal
@view
def get_xy_up(user: address, use_y: bool) -> uint256:
    """
    @notice Measure the amount of y (collateral) in the band n if we adiabatically trade near p_oracle on the way up,
            or the amount of x (stablecoin) if we trade adiabatically down
    @param user User the amount is calculated for
    @param use_y Calculate amount of collateral if True and of stablecoin if False
    @return Amount of coins
    """
    ns: int256[2] = self._read_user_tick_numbers(user)
    ticks: DynArray[uint256, MAX_TICKS_UINT] = self._read_user_ticks(user, ns)
    if ticks[0] == 0:  # Even dynamic array will have 0th element set here
        return 0
    p_o: uint256 = self._price_oracle_ro()[0]
    assert p_o != 0

    n: int256 = ns[0] - 1
    n_active: int256 = self.active_band
    p_o_down: uint256 = self._p_oracle_up(ns[0])
    XY: uint256 = 0

    for i in range(MAX_TICKS):
        n += 1
        if n > ns[1]:
            break
        x: uint256 = 0
        y: uint256 = 0
        if n >= n_active:
            y = self.bands_y[n]
        if n <= n_active:
            x = self.bands_x[n]
        # p_o_up: uint256 = self._p_oracle_up(n)
        p_o_up: uint256 = p_o_down
        # p_o_down = self._p_oracle_up(n + 1)
        p_o_down = unsafe_div(p_o_down * Aminus1, A)
        if x == 0:
            if y == 0:
                continue

        total_share: uint256 = self.total_shares[n]
        user_share: uint256 = ticks[i]
        if total_share == 0:
            continue
        if user_share == 0:
            continue
        total_share += DEAD_SHARES
        # Also ideally we'd want to add +1 to all quantities when calculating with shares
        # but we choose to save bytespace and slightly under-estimate the result of this call
        # which is also more conservative

        # Also this will revert if p_o_down is 0, and p_o_down is 0 if p_o_up is 0
        p_current_mid: uint256 = unsafe_div(p_o**2 / p_o_down * p_o, p_o_up)

        # if p_o > p_o_up - we "trade" everything to y and then convert to the result
        # if p_o < p_o_down - "trade" to x, then convert to result
        # otherwise we are in-band, so we do the more complex logic to trade
        # to p_o rather than to the edge of the band
        # trade to the edge of the band == getting to the band edge while p_o=const

        # Cases when special conversion is not needed (to save on computations)
        if x == 0 or y == 0:
            if p_o > p_o_up:  # p_o < p_current_down
                # all to y at constant p_o, then to target currency adiabatically
                y_equiv: uint256 = y
                if y == 0:
                    y_equiv = x * 10**18 / p_current_mid
                if use_y:
                    XY += unsafe_div(y_equiv * user_share, total_share)
                else:
                    XY += unsafe_div(unsafe_div(y_equiv * p_o_up, SQRT_BAND_RATIO) * user_share, total_share)
                continue

            elif p_o < p_o_down:  # p_o > p_current_up
                # all to x at constant p_o, then to target currency adiabatically
                x_equiv: uint256 = x
                if x == 0:
                    x_equiv = unsafe_div(y * p_current_mid, 10**18)
                if use_y:
                    XY += unsafe_div(unsafe_div(x_equiv * SQRT_BAND_RATIO, p_o_up) * user_share, total_share)
                else:
                    XY += unsafe_div(x_equiv * user_share, total_share)
                continue

        # If we are here - we need to "trade" to somewhere mid-band
        # So we need more heavy math

        y0: uint256 = self._get_y0(x, y, p_o, p_o_up)
        f: uint256 = unsafe_div(unsafe_div(A * y0 * p_o, p_o_up) * p_o, 10**18)
        g: uint256 = unsafe_div(Aminus1 * y0 * p_o_up, p_o)
        # (f + x)(g + y) = const = p_top * A**2 * y0**2 = I
        Inv: uint256 = (f + x) * (g + y)
        # p = (f + x) / (g + y) => p * (g + y)**2 = I or (f + x)**2 / p = I

        # First, "trade" in this band to p_oracle
        x_o: uint256 = 0
        y_o: uint256 = 0

        if p_o > p_o_up:  # p_o < p_current_down, all to y
            # x_o = 0
            y_o = unsafe_sub(max(Inv / f, g), g)
            if use_y:
                XY += unsafe_div(y_o * user_share, total_share)
            else:
                XY += unsafe_div(unsafe_div(y_o * p_o_up, SQRT_BAND_RATIO) * user_share, total_share)

        elif p_o < p_o_down:  # p_o > p_current_up, all to x
            # y_o = 0
            x_o = unsafe_sub(max(Inv / g, f), f)
            if use_y:
                XY += unsafe_div(unsafe_div(x_o * SQRT_BAND_RATIO, p_o_up) * user_share, total_share)
            else:
                XY += unsafe_div(x_o * user_share, total_share)

        else:
            # Equivalent from Chainsecurity (which also has less numerical errors):
            y_o = unsafe_div(A * y0 * unsafe_sub(p_o, p_o_down), p_o)
            # x_o = unsafe_div(A * y0 * p_o, p_o_up) * unsafe_sub(p_o_up, p_o)
            # Old math
            # y_o = unsafe_sub(max(self.sqrt_int(unsafe_div(Inv * 10**18, p_o)), g), g)
            x_o = unsafe_sub(max(Inv / (g + y_o), f), f)

            # Now adiabatic conversion from definitely in-band
            if use_y:
                XY += unsafe_div((y_o + x_o * 10**18 / self.sqrt_int(p_o_up * p_o)) * user_share, total_share)

            else:
                XY += unsafe_div((x_o + unsafe_div(y_o * self.sqrt_int(p_o_down * p_o), 10**18)) * user_share, total_share)

    if use_y:
        return unsafe_div(XY, COLLATERAL_PRECISION)
    else:
        return unsafe_div(XY, BORROWED_PRECISION)


@external
@view
@nonreentrant('lock')
def get_y_up(user: address) -> uint256:
    """
    @notice Measure the amount of y (collateral) in the band n if we adiabatically trade near p_oracle on the way up
    @param user User the amount is calculated for
    @return Amount of coins
    """
    return self.get_xy_up(user, True)


@external
@view
@nonreentrant('lock')
def get_x_down(user: address) -> uint256:
    """
    @notice Measure the amount of x (stablecoin) if we trade adiabatically down
    @param user User the amount is calculated for
    @return Amount of coins
    """
    return self.get_xy_up(user, False)

@internal
@view
def _get_xy(user: address, is_sum: bool) -> DynArray[uint256, MAX_TICKS_UINT][2]:
    """
    @notice A low-gas function to measure amounts of stablecoins and collateral which user currently owns
    @param user User address
    @param is_sum Return sum or amounts by bands
    @return Amounts of (stablecoin, collateral) in a tuple
    """
    xs: DynArray[uint256, MAX_TICKS_UINT] = []
    ys: DynArray[uint256, MAX_TICKS_UINT] = []
    if is_sum:
        xs.append(0)
        ys.append(0)
    ns: int256[2] = self._read_user_tick_numbers(user)
    ticks: DynArray[uint256, MAX_TICKS_UINT] = self._read_user_ticks(user, ns)
    if ticks[0] != 0:
        for i in range(MAX_TICKS):
            total_shares: uint256 = self.total_shares[ns[0]] + DEAD_SHARES
            ds: uint256 = ticks[i]
            dx: uint256 = unsafe_div((self.bands_x[ns[0]] + 1) * ds, total_shares)
            dy: uint256 = unsafe_div((self.bands_y[ns[0]] + 1) * ds, total_shares)
            if is_sum:
                xs[0] += dx
                ys[0] += dy
            else:
                xs.append(unsafe_div(dx, BORROWED_PRECISION))
                ys.append(unsafe_div(dy, COLLATERAL_PRECISION))
            if ns[0] == ns[1]:
                break
            ns[0] = unsafe_add(ns[0], 1)

    if is_sum:
        xs[0] = unsafe_div(xs[0], BORROWED_PRECISION)
        ys[0] = unsafe_div(ys[0], COLLATERAL_PRECISION)

    return [xs, ys]

@external
@view
@nonreentrant('lock')
def get_sum_xy(user: address) -> uint256[2]:
    """
    @notice A low-gas function to measure amounts of stablecoins and collateral which user currently owns
    @param user User address
    @return Amounts of (stablecoin, collateral) in a tuple
    """
    xy: DynArray[uint256, MAX_TICKS_UINT][2] = self._get_xy(user, True)
    return [xy[0][0], xy[1][0]]

@external
@view
@nonreentrant('lock')
def get_xy(user: address) -> DynArray[uint256, MAX_TICKS_UINT][2]:
    """
    @notice A low-gas function to measure amounts of stablecoins and collateral by bands which user currently owns
    @param user User address
    @return Amounts of (stablecoin, collateral) by bands in a tuple
    """
    return self._get_xy(user, False)


@external
@view
@nonreentrant('lock')
def get_amount_for_price(p: uint256) -> (uint256, bool):
    """
    @notice Amount necessary to be exchanged to have the AMM at the final price `p`
    @return (amount, is_pump)
    """
    min_band: int256 = self.min_band
    max_band: int256 = self.max_band
    n: int256 = self.active_band
    p_o: uint256[2] = self._price_oracle_ro()
    p_o_up: uint256 = self._p_oracle_up(n)
    p_down: uint256 = unsafe_div(unsafe_div(p_o[0]**2, p_o_up) * p_o[0], p_o_up)  # p_current_down
    p_up: uint256 = unsafe_div(p_down * A2, Aminus12)  # p_crurrent_up
    amount: uint256 = 0
    y0: uint256 = 0
    f: uint256 = 0
    g: uint256 = 0
    Inv: uint256 = 0
    j: uint256 = MAX_TICKS_UINT
    pump: bool = True

    for i in range(MAX_TICKS + MAX_SKIP_TICKS):
        assert p_o_up > 0
        x: uint256 = self.bands_x[n]
        y: uint256 = self.bands_y[n]
        if i == 0:
            if p < self._get_p(n, x, y):
                pump = False
        not_empty: bool = x > 0 or y > 0
        if not_empty:
            y0 = self._get_y0(x, y, p_o[0], p_o_up)
            f = unsafe_div(unsafe_div(A * y0 * p_o[0], p_o_up) * p_o[0], 10**18)
            g = unsafe_div(Aminus1 * y0 * p_o_up, p_o[0])
            Inv = (f + x) * (g + y)
            if j == MAX_TICKS_UINT:
                j = 0

        if p <= p_up:
            if p >= p_down:
                if not_empty:
                    ynew: uint256 = unsafe_sub(max(self.sqrt_int(Inv * 10**18 / p), g), g)
                    xnew: uint256 = unsafe_sub(max(Inv / (g + ynew), f), f)
                    if pump:
                        amount += unsafe_sub(max(xnew, x), x)
                    else:
                        amount += unsafe_sub(max(ynew, y), y)
                break

        # Need this to break if price is too far
        p_ratio: uint256 = unsafe_div(p_o_up * 10**18, p_o[0])

        if pump:
            if not_empty:
                amount += (Inv / g - f) - x
            if n == max_band:
                break
            if j == MAX_TICKS_UINT - 1:
                break
            if p_ratio < unsafe_div(10**36, MAX_ORACLE_DN_POW):
                # Don't allow to be away by more than ~50 ticks
                break
            n += 1
            p_down = p_up
            p_up = unsafe_div(p_up * A2, Aminus12)
            p_o_up = unsafe_div(p_o_up * Aminus1, A)

        else:
            if not_empty:
                amount += (Inv / f - g) - y
            if n == min_band:
                break
            if j == MAX_TICKS_UINT - 1:
                break
            if p_ratio > MAX_ORACLE_DN_POW:
                # Don't allow to be away by more than ~50 ticks
                break
            n -= 1
            p_up = p_down
            p_down = unsafe_div(p_down * Aminus12, A2)
            p_o_up = unsafe_div(p_o_up * A, Aminus1)

        if j != MAX_TICKS_UINT:
            j = unsafe_add(j, 1)

    amount = amount * 10**18 / unsafe_sub(10**18, max(self.fee, p_o[1]))
    if amount == 0:
        return 0, pump

    # Precision and round up
    if pump:
        amount = unsafe_add(unsafe_div(unsafe_sub(amount, 1), BORROWED_PRECISION), 1)
    else:
        amount = unsafe_add(unsafe_div(unsafe_sub(amount, 1), COLLATERAL_PRECISION), 1)

    return amount, pump


@external
@nonreentrant('lock')
def set_rate(rate: uint256) -> uint256:
    """
    @notice Set interest rate. That affects the dependence of AMM base price over time
    @param rate New rate in units of int(fraction * 1e18) per second
    @return rate_mul multiplier (e.g. 1.0 + integral(rate, dt))
    """
    assert msg.sender == self.admin
    rate_mul: uint256 = self._rate_mul()
    self.rate_mul = rate_mul
    self.rate_time = block.timestamp
    self.rate = rate
    log SetRate(rate, rate_mul, block.timestamp)
    return rate_mul


@external
@nonreentrant('lock')
def set_fee(fee: uint256):
    """
    @notice Set AMM fee
    @param fee Fee where 1e18 == 100%
    """
    assert msg.sender == self.admin
    self.fee = fee
    log SetFee(fee)


@external
@nonreentrant('lock')
def set_admin_fee(fee: uint256):
    """
    @notice Set admin fee - fraction of the AMM fee to go to admin
    @param fee Admin fee where 1e18 == 100%
    """
    assert msg.sender == self.admin
    self.admin_fee = fee
    log SetAdminFee(fee)


@external
@nonreentrant('lock')
def reset_admin_fees():
    """
    @notice Zero out AMM fees collected
    """
    assert msg.sender == self.admin
    self.admin_fees_x = 0
    self.admin_fees_y = 0


# nonreentrant decorator is in Controller which is admin
@external
def set_callback(liquidity_mining_callback: LMGauge):
    """
    @notice Set a gauge address with callbacks for liquidity mining for collateral
    @param liquidity_mining_callback Gauge address
    """
    assert msg.sender == self.admin
    self.liquidity_mining_callback = liquidity_mining_callback

Contract Security Audit

Contract ABI

[{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"uint256","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"uint256","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Deposit","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"amount","type":"uint256","indexed":false},{"name":"n1","type":"int256","indexed":false},{"name":"n2","type":"int256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Withdraw","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"amount_borrowed","type":"uint256","indexed":false},{"name":"amount_collateral","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetRate","inputs":[{"name":"rate","type":"uint256","indexed":false},{"name":"rate_mul","type":"uint256","indexed":false},{"name":"time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetFee","inputs":[{"name":"fee","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetAdminFee","inputs":[{"name":"fee","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_borrowed_token","type":"address"},{"name":"_borrowed_precision","type":"uint256"},{"name":"_collateral_token","type":"address"},{"name":"_collateral_precision","type":"uint256"},{"name":"_A","type":"uint256"},{"name":"_sqrt_band_ratio","type":"uint256"},{"name":"_log_A_ratio","type":"int256"},{"name":"_base_price","type":"uint256"},{"name":"fee","type":"uint256"},{"name":"admin_fee","type":"uint256"},{"name":"_price_oracle_contract","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_admin","inputs":[{"name":"_admin","type":"address"}],"outputs":[]},{"stateMutability":"pure","type":"function","name":"coins","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"price_oracle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"dynamic_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_rate_mul","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_base_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"p_current_up","inputs":[{"name":"n","type":"int256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"p_current_down","inputs":[{"name":"n","type":"int256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"p_oracle_up","inputs":[{"name":"n","type":"int256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"p_oracle_down","inputs":[{"name":"n","type":"int256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_p","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"read_user_tick_numbers","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"int256[2]"}]},{"stateMutability":"view","type":"function","name":"can_skip_bands","inputs":[{"name":"n_end","type":"int256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"active_band_with_skip","inputs":[],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"view","type":"function","name":"has_liquidity","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"deposit_range","inputs":[{"name":"user","type":"address"},{"name":"amount","type":"uint256"},{"name":"n1","type":"int256"},{"name":"n2","type":"int256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"withdraw","inputs":[{"name":"user","type":"address"},{"name":"frac","type":"uint256"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"in_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dxdy","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"in_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dx","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"out_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dydx","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"out_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"in_amount","type":"uint256"},{"name":"min_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"in_amount","type":"uint256"},{"name":"min_amount","type":"uint256"},{"name":"_for","type":"address"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_dy","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"out_amount","type":"uint256"},{"name":"max_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_dy","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"out_amount","type":"uint256"},{"name":"max_amount","type":"uint256"},{"name":"_for","type":"address"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"view","type":"function","name":"get_y_up","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_x_down","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_sum_xy","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"view","type":"function","name":"get_xy","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256[][2]"}]},{"stateMutability":"view","type":"function","name":"get_amount_for_price","inputs":[{"name":"p","type":"uint256"}],"outputs":[{"name":"","type":"uint256"},{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"set_rate","inputs":[{"name":"rate","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"set_fee","inputs":[{"name":"fee","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_admin_fee","inputs":[{"name":"fee","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"reset_admin_fees","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_callback","inputs":[{"name":"liquidity_mining_callback","type":"address"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"admin","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"rate","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"active_band","inputs":[],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"view","type":"function","name":"min_band","inputs":[],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"view","type":"function","name":"max_band","inputs":[],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"view","type":"function","name":"admin_fees_x","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"admin_fees_y","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_oracle_contract","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"bands_x","inputs":[{"name":"arg0","type":"int256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"bands_y","inputs":[{"name":"arg0","type":"int256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"liquidity_mining_callback","inputs":[],"outputs":[{"name":"","type":"address"}]}]

615d75515034610184576020615d945f395f518060a01c610184576040526020615dd45f395f518060a01c610184576060526020615ed45f395f518060a01c61018457608052604051615bf5526020615db45f395f51615c1552606051615c35526020615df45f395f51615c55526020615e145f395f51615c95526020615e745f395f51615c75526001615c955103615cb5526002615c95510a615cd55260026001615c9551030a615cf5526020615e945f395f516002556020615eb45f395f51600355608051615d755242600e55615d755163a035b1fe60a052602060a0600460bc845afa6100f1573d5f5f3e3d5ffd5b60203d106101845760a0905051600c55670de0b6b3a76400006006556020615e345f395f51615d15526020615e545f395f51615d3552670de0b6b3a764000060a0525f6032905b8060c052615cb55160a051615c955180820281158383830414171561018457905090500460a05260010181811861013857505060a051615d5552615bf561018861000039615d95610000f35b5f80fd5f3560e01c6002602d820660011b615b9b01601e395f51565b63f851a44081186121605734615b975760015460405260206040f3612160565b63f446c1d081186100565734615b97576020615c9560403960206040f35b635ea0e01b81186121605734615b97576020615d7560403960206040f3612160565b63ddca3f4381186100945734615b975760025460405260206040f35b633c10269a81186100f15760a436103417615b97576084358060a01c615b9757612240525b5f54600214615b975760025f55604060806004610bc03761224051610c40525f610c60526100e86122606147bf565b61226060035f55f35b6348e995f9811861216057602436103417615b97575f54600214615b9757600854610280526009546102a0526007546102c05261012f610320612584565b61032080516102e052602081015161030052506102c051606052610154610340612703565b610340516103205261032051610320516102e0516fffffffffffffffffffffffffffffffff8111615b97576002810a9050046102e051808202811583838304141715615b97579050905004610340526020615cf55f395f51610340516020615cd55f395f51808202811583838304141715615b975790509050046103605260a036610380376032610420526001610440525f610432905b80610460526103205115615b9757600f6102c0516020525f5260405f20546104805260106102c0516020525f5260405f20546104a0526104605161025f576102c05161018052610480516101a0526104a0516101c05261024c6104c0612ce5565b6104c051600435101561025f575f610440525b610480511561026f576001610276565b6104a05115155b6104c0526104c051156103ad57610480516060526104a0516080526102e05160a0526103205160c0526102aa6104e0612acb565b6104e0516103a052670de0b6b3a7640000610320516020615c955f395f516103a051808202811583838304141715615b9757905090506102e051808202811583838304141715615b975790509050046102e051808202811583838304141715615b975790509050046103c0526102e0516020615cb55f395f516103a051808202811583838304141715615b97579050905061032051808202811583838304141715615b975790509050046103e0526103c05161048051808201828110615b9757905090506103e0516104a051808201828110615b975790509050808202811583838304141715615b97579050905061040052603261042051186103ad575f610420525b61036051600435116104df5761034051600435106104df576104c05115610767576103e05161040051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790506004358015615b9757808204905090506040526104126105006121ea565b610500516103e05180828118828411021890509050036104e0526103c051610400516103e0516104e051808201828110615b9757905090508015615b9757808204905090506103c051808281188284110218905090500361050052610440516104aa57610380516104a0516104e0516104a0518082811882841102189050905003808201828110615b97579050905061038052610767565b610380516104805161050051610480518082811882841102189050905003808201828110615b97579050905061038052610767565b6102e05161032051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050046104e05261044051610625576104c0511561056b5761038051610400516103c0518015615b9757808204905090506103e051808203828111615b9757905090506104a051808203828111615b975790509050808201828110615b975790509050610380525b610280516102c0511861057d57610767565b6031610420511861058d57610767565b6020615d555f395f516104e05111156105a557610767565b6102c05160018103818113615b975790506102c05261034051610360526020615cd55f395f51610340516020615cf55f395f51808202811583838304141715615b97579050905004610340526020615cb55f395f51610320516020615c955f395f51808202811583838304141715615b9757905090500461032052610745565b6104c0511561067e5761038051610400516103e0518015615b9757808204905090506103c051808203828111615b97579050905061048051808203828111615b975790509050808201828110615b975790509050610380525b6102a0516102c0511861069057610767565b603161042051186106a057610767565b6020615d555f395f516ec097ce7bc90715b34b9f1000000000046104e05110156106c957610767565b6102c05160018101818112615b975790506102c05261036051610340526020615cf55f395f51610360516020615cd55f395f51808202811583838304141715615b97579050905004610360526020615c955f395f51610320516020615cb55f395f51808202811583838304141715615b97579050905004610320525b6032610420511461075c5760016104205101610420525b6001018181186101eb575b505061038051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790506002546103005180828118828411021890509050670de0b6b3a7640000038015615b97578082049050905061038052610380516107d9575f610460526104405161048052604061046061082d565b610440516107fe5760016020615c555f395f5160016103805103040161038052610817565b60016020615c155f395f51600161038051030401610380525b6103805161046052610440516104805260406104605bf3612160565b63fee3f7f9811861084f5734615b975760035460405260206040f35b63ee4c32ee811861216057602436103417615b97576004358060a01c615b97576114a0525f54600214615b975760206114a051610760526001610780526108976114c0614f50565b6114c0f3612160565b632c4e722e81186121605734615b975760045460405260206040f3612160565b638f8654c581186108dc5734615b975760075460405260206040f35b63ed7110cf811861093c57606436103417615b97575f54600214615b975760606004610bc0375f610c20526109126122206145a4565b612220610720611b006107208360045afa5050611b205161222052611b0051612240526040612220f35b63d4387a99811861216057602436103417615b97575f54600214615b975760025f556001543318615b9757610971606061265f565b606051604052604051600655426005556004356004557f52543716810f73c3fa9bca74622aecb6d3614ca4991472f3e999d531c2f6afb86004356060526040516080524260a05260606060a16020604060035f55f3612160565b63ca72a82181186121605734615b975760085460405260206040f3612160565b63aaa615fc8118610a075734615b975760095460405260206040f35b6324299b7a8118610a4157602436103417615b9757602060043560018101818112615b97579050606052610a3c610180612703565b610180f35b63f2388acb81186121605734615b97575f54600214615b97576007546102805260206102805161018052600f610280516020525f5260405f20546101a0526010610280516020525f5260405f20546101c052610a9e6102a0612ce5565b6102a0f3612160565b63d1fea73381186121605734615b9757600a5460405260206040f3612160565b6389960ba781186121605734615b9757600b5460405260206040f3612160565b63ebcb0067811861216057602436103417615b9757600f6004356020525f5260405f205460405260206040f3612160565b6331f7e306811861216057602436103417615b975760106004356020525f5260405f205460405260206040f3612160565b63611105d38118610b655734615b975760135460405260206040f35b63c16ef26481186121605734615b97575f54600214615b97576007546040526040516060526008546080525f610400905b8060a0526080516060511215610bbf576040516104008103818113615b97579050606052610bf6565b600f6060516020525f5260405f205415610bd857610bf6565b60605160018103818113615b97579050606052600101818118610b96575b505060206060f3612160565b63e9333fab8118610c6457602436103417615b97576004358060a01c615b975761010052600154615b9757610100516001556020615bf560403961010051606052610c4b612164565b6020615c3560403961010051606052610c62612164565b005b63822fe50781186121605734615b97575f54600214615b975760025f556001543318615b97575f600a555f600b5560035f5500612160565b63c66106578118610cd957602436103417615b975760206020615bf56040396020615c35606039604060043560018111615b975760051b81019050f35b6386fc88d38118610cfb5734615b97576020610cf6610180612584565b610180f35b63556d6e9f811861216057606436103417615b97575f54600214615b9757602060606004610bc0376001610c2052610d34611b006145a4565b611b00602081019050f3612160565b6377c345948118610d835734615b9757600254610d61610180612584565b61018060208101905051808281188284110218905090506101c05260206101c0f35b63b461100d8118610dc557602436103417615b97576004358060a01c615b975760c0525f54600214615b9757604060c051604052610dc160e0612f9e565b60e0f35b63a3e346ec811861216057608436103417615b975733612240526100b956612160565b63095a0fc68118610e085734615b97576020610e04604061265f565b6040f35b63544fb5c1811861216057602436103417615b97576004358060a01c615b9757612160525f54600214615b97576121605161076052600161078052610e4e612e4061588c565b612e40610cc0612180610cc08360045afa50506121805115615b97575f60051b6121a00151612e40526127e05115615b97575f60051b6128000151612e60526040612e40f3612160565b63a7db79a581186121605734615b97576020610eb460606126ca565b6060f3612160565b637c1bbd83811861216057602436103417615b9757602060043560018101818112615b9757905061018052610ef2610220612a4a565b610220f3612160565b63c32bd03c811861216057602436103417615b9757602060043561018052610f24610220612a4a565b610220f3612160565b632eb858e78118610f5a57602436103417615b97576020600435606052610f55610180612703565b610180f35b63ec654706811861101a57602436103417615b97575f54600214615b97576007546040525f610400905b8060605260405160043513610fc457600f6040516020525f5260405f205415610fb65750505f60805260206080611018565b600160405103604052610ff1565b60106040516020525f5260405f205415610fe75750505f60805260206080611018565b6001604051016040525b600435604051186110015761100c565b600101818118610f84575b50506001606052602060605bf35b6362ca4b18811861216057602436103417615b97576004358060a01c615b97576114a0525f54600214615b975760206114a051610760525f610780526110616114c0614f50565b6114c0f3612160565b63e8dd1ef181186110b657602436103417615b97576004358060a01c615b97576040525f54600214615b975760126040516020525f5260405f2060018101905054151560605260206060f35b635b41b908811861216057608436103417615b97573361224052611f0f56612160565b63ab047e00811460033611161561216057608436103417615b97576004358060a01c615b9757610720525f54600214615b975760025f556001543318615b97575f610740525f610da052600754611400526f7fffffffffffffffffffffffffffffff60643513615b97577fffffffffffffffffffffffffffffffff8000000000000000000000000000000160443512615b97576001604435606435035f8112615b9757016114205260326114205111615b9757611420516024356020615c555f395f51808202811583838304141715615b9757905090500461144052606561144051101561122657600e611460527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006114805261146050611460518061148001601f825f031636823750506308c379a061142052602061144052601f19601f61146051011660440161143cfd5b6012610720516020525f5260405f2060018101905054615b975760643560801b604435016012610720516020525f5260405f2055601354611460525f610401905b806114805261140051604435131561128e57611480511561133b576114005160075561133b565b600f611400516020525f5260405f20546112b0576103ff6114805113156112b2565b5f5b61131b57601a6114a0527f4465706f7369742062656c6f772063757272656e742062616e640000000000006114c0526114a0506114a051806114c001601f825f031636823750506308c379a061146052602061148052601f19601f6114a051011660440161147cfd5b6114005160018103818113615b9757905061140052600101818118611267575b50505f6032905b806114805261148051604435016114a0526064356114a051131561136557611608565b600f6114a0516020525f5260405f2054156113df57600e6114c0527f42616e64206e6f7420656d7074790000000000000000000000000000000000006114e0526114c0506114c051806114e001601f825f031636823750506308c379a06114805260206114a052601f19601f6114c051011660440161149cfd5b611440516114c05261148051611441576024356020615c555f395f51808202811583838304141715615b9757905090506114c05160016114205103808202811583838304141715615b975790509050808203828111615b9757905090506114c0525b60106114a0516020525f5260405f20546114e05260116114a0516020525f5260405f2054611500526114e05160018101818110615b97579050611500516103e88101818110615b975790506114c051808202811583838304141715615b97579050905004611520526115205161151657600e611540527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006115605261154050611540518061156001601f825f031636823750506308c379a061150052602061152052601f19601f61154051011660440161151cfd5b6107405160318111615b9757611520518160051b61076001526001810161074052506115005161152051808201828110615b975790509050611500526fffffffffffffffffffffffffffffffff6115005111615b97576115005160116114a0516020525f5260405f20556114e0516114c051808201828110615b9757905090506114e0526114e05160106114a0516020525f5260405f205561146051156115fd57610da05160318111615b9757611500516114e051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050048160051b610dc0015260018101610da052505b600101818118611342575b50506008546044358082811882841202189050905060085560095460643580828118828413021890509050600955610720516040526107405160208160051b018060608261074060045afa50505061165e613136565b610720517f7e4f5fadb3361b33669433b392d1a203b7a236710eb272650052592e6ce62f0960606024611480376060611480a261146051156117bf576114605163d1c923536114805260406044356114a052806114c052806114a0015f610da0518083528060051b5f8260328111615b975780156116f657905b8060051b610dc001518160051b6020880101526001018181186116d8575b50508201602001915050905081015050803b15615b97575f6114806106a461149c5f855af1611727573d5f5f3e3d5ffd5b506114605163f9d0ca12611480526060610720516114a0526044356114c052806114e052806114a0015f610740518083528060051b5f8260328111615b9757801561178c57905b8060051b61076001518160051b60208801015260010181811861176e575b50508201602001915050905081015050803b15615b97575f6114806106c461149c5f855af16117bd573d5f5f3e3d5ffd5b505b60035f5500612160565b63f3fef3a3811861216057604436103417615b97576004358060a01c615b9757610760525f54600214615b975760025f556001543318615b9757670de0b6b3a764000060243511615b9757601354610780526107605160405261182d6107e0612f9e565b6107e080516107a05260208101516107c052506107a0516107e052610760516040526107a0516060526107c051608052611868610e60613031565b610e60805160208160051b0180610800828560045afa505050506108005115615b97575f60051b61082001516118fd57600b610e60527f4e6f206465706f73697473000000000000000000000000000000000000000000610e8052610e6050610e605180610e8001601f825f031636823750506308c379a0610e20526020610e4052601f19601f610e60510116604401610e3cfd5b604036610e6037600854610ea052610ea051610ec052600954610ee0526107e05160018103818113615b97579050610f00525f6032905b80610f2052600f6107e0516020525f5260405f2054610f405260106107e0516020525f5260405f2054610f6052670de0b6b3a7640000602435610f205161080051811015615b975760051b6108200151808202811583838304141715615b97579050905004610f8052610f8051610f205161080051811015615b975760051b610820015103610f205161080051811015615b975760051b610820015260116107e0516020525f5260405f2054610fa052610fa051610f8051808203828111615b975790509050610fc052610fc05160116107e0516020525f5260405f2055610fa0516103e88101818110615b97579050610fa052610fa051610f405160018101818110615b97579050610f8051808202811583838304141715615b97579050905004610fe052610fa051610f605160018101818110615b97579050610f8051808202811583838304141715615b9757905090500461100052610f4051610fe051808203828111615b975790509050610f4052610f605161100051808203828111615b975790509050610f6052610fc051611b2857610f405115611af457600a546020615c155f395f51610f405104808201828110615b975790509050600a555b610f605115611b2057600b546020615c555f395f51610f605104808201828110615b975790509050600b555b604036610f40375b610ea0516107e05118611b5b57610f4051611b5b57610f6051611b5b57610ea05160018101818112615b97579050610ea0525b610f405115611b6b576001611b72565b610f605115155b15611b80576107e051610f00525b610f4051600f6107e0516020525f5260405f2055610f605160106107e0516020525f5260405f2055610e6051610fe051808201828110615b975790509050610e6052610e805161100051808201828110615b975790509050610e80526107c0516107e05118611bf257611c0956611bfe565b60016107e051016107e0525b600101818118611934575b5050670de0b6b3a764000060243518611c38575f6012610760516020525f5260405f2060018101905055611c60565b610760516040526108005160208160051b018060608261080060045afa505050611c60613136565b610ea051610ec05114611c7557610ea0516008555b6107c051610ee05113611c8a57610f00516009555b6020615c155f395f51610e605104610e60526020615c555f395f51610e805104610e8052610760517ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568610e6051610f2052610e8051610f40526040610f20a26107805115611e06576107805163d1c92353610f205260405f610f405280610f605280610f40015f5f82525f5f5f60328111615b97578015611d3d57905b5f8160051b602087010152600101818118611d27575b505081016020019050905081015050803b15615b97575f610f206106a4610f3c5f855af1611d6d573d5f5f3e3d5ffd5b506107805163f9d0ca12610f2052606061076051610f40526107a051610f605280610f805280610f40015f610800518083528060051b5f8260328111615b97578015611dd357905b8060051b61082001518160051b602088010152600101818118611db5575b50508201602001915050905081015050803b15615b97575f610f206106c4610f3c5f855af1611e04573d5f5f3e3d5ffd5b505b610e6051610f2052610e8051610f40526040610f2060035f55f3612160565b63c49202e7811861216057606436103417615b97575f54600214615b975760606004610bc0376001610c2052611e5c6122206145a4565b612220610720611b006107208360045afa5050611b005161222052611b2051612240526040612220f3612160565b6337ed3a7a811861216057606436103417615b97575f54600214615b975760606004610bc0375f610c2052611ec06122206145a4565b612220610720611b006107208360045afa5050604435611b205118615b97576020611b00f3612160565b63a64833a081186121605760a436103417615b97576084358060a01c615b9757612240525b5f54600214615b975760025f55604060806004610bc03761224051610c40526001610c6052611f3f6122606147bf565b61226060035f55f3612160565b6384738380811861216057602436103417615b97576004358060a01c615b9757612160525f54600214615b9757602080612e405261216051610760525f61078052611f9861218061588c565b61218081612e400160408082528082015f84518083528060051b5f8260328111615b97578015611fe357905b8060051b60208a0101518160051b602088010152600101818118611fc4575b5050820160200191505090508101905080602083015261066083018183015f82518083528060051b5f8260328111615b9757801561203c57905b8060051b6020880101518160051b60208801015260010181811861201d575b5050820160200191505090509050810190509050905081019050612e40f3612160565b631aa02d59811861216057602436103417615b97575f54600214615b975760025f556001543318615b97576004356002557e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a760043560405260206040a160035f5500612160565b633217902f811861216057602436103417615b97575f54600214615b975760025f556001543318615b97576004356003557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b7460043560405260206040a160035f5500612160565b63cc1891c7811861216057602436103417615b97576004358060a01c615b97576040526001543318615b9757604051601355005b5f5ffd5b60405163095ea7b360805260605160a0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60c052602060806044609c5f855af16121b1573d5f5f3e3d5ffd5b3d6121c757803b15615b9757600160e0526121de565b60203d10615b97576080518060011c615b975760e0525b60e090505115615b9757565b6040518060b5710100000000000000000000000000000000008210612216578160801c91508060401b90505b69010000000000000000008210612234578160401c91508060201b90505b65010000000000821061224e578160201c91508060101b90505b63010000008210612266578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050815250565b60405160605242600e54808203828111615b975790509050806078811882607810021890506078036080525f60a0526080511561245757600c5460c052600d5460e05260c051604051116123985760c051604051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790500460a052670b1a2bc2ec4fffff60a0511161240c57671158e460913d000060c051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004606052670b1a2bc2ec50000060a05261240c565b60405160c051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790500460a052670b1a2bc2ec4fffff60a0511161240c57670de0b6b3a764000060c051671158e460913d0000810281671158e460913d0000820418615b9757905004606052670b1a2bc2ec50000060a0525b60786080516ec097ce7bc90715b34b9f1000000000600360a0510a0460e051670de0b6b3a764000001030204670de0b6b3a763ffff818118670de0b6b3a763ffff83100218905060a0525b606051815260a051602082015250565b6060516060516040516fffffffffffffffffffffffffffffffff8111615b97576002810a905004604051808202811583838304141715615b975790509050046080526020615cb55f395f516020615cb55f395f516080516020615c955f395f51808202811583838304141715615b975790509050046020615c955f395f51808202811583838304141715615b9757905090500460a052608051604051106125555760a05160405111612520575f81525061258256612582565b60405160a051604051036703782dace9d900008102816703782dace9d90000820418615b975790500481525061258256612582565b608051604051608051036703782dace9d900008102816703782dace9d90000820418615b97579050048152505b565b6020615d755f395f5163a035b1fe610100526020610100600461011c845afa6125af573d5f5f3e3d5ffd5b60203d10615b97576101009050516040526125cb6101406122d2565b61014080518252602081015160208301525050565b6020615d755f395f5163ceb7f759610140526020610140600461015c5f855af161260c573d5f5f3e3d5ffd5b60203d10615b97576101409050516040526126286101806122d2565b6101808051610100526020810151610120525042600e5561010051600c5561012051600d5561010051815261012051602082015250565b670de0b6b3a764000060065460045442600554808203828111615b975790509050808202811583838304141715615b97579050905080670de0b6b3a764000001670de0b6b3a76400008110615b97579050808202811583838304141715615b97579050905004815250565b670de0b6b3a76400006020615c755f395f516126e6604061265f565b604051808202811583838304141715615b97579050905004815250565b6060517f80000000000000000000000000000000000000000000000000000000000000008114615b97575f036020615d355f395f5180820281191515600160ff1b8414151782158484840514171615615b9757905090506080527ffffffffffffffffffffffffffffffffffffffffffffffffdc0d0570925a4668160805112615b9757680755bf798b4a1bf1e460805113615b9757670de0b6b3a764000060805160601b0560a0526c010000000000000000000000006b8000000000000000000000006bb17217f7d1cf79abc9e3b39860a05160601b05010560c0526bb17217f7d1cf79abc9e3b39860c0510260a0510360a0526c10fe68e7fd37d0007b713f765060a0510160e0526d02d16720577bd19bf614176fe9ea6c0100000000000000000000000060a05160e05102050160e0526d04a4fd9f2a8b96949216d2255a6c60a05160e0510103610100526e0587f503bb6ea29d25fcb7401964506c0100000000000000000000000060e051610100510205016101005279d835ebba824c98fb31b83b2ca45c00000000000000000000000060a0516101005102016101005260a0516c240c330e9fb2d9cbaf0fd5aafc8103818113615b97579050610120526d0277594991cfc85f6e2461837cd96c0100000000000000000000000060a05161012051020501610120526d1a521255e34f6a5061b25ef1c9c46c0100000000000000000000000060a05161012051020503610120526db1bbb201f443cf962f1a1d3db4a56c0100000000000000000000000060a05161012051020501610120526e02c72388d9f74f51a9331fed693f156c0100000000000000000000000060a05161012051020503610120526e05180bb14799ab47a8a8cb2a527d576c0100000000000000000000000060a051610120510205016101205274029d9dc38563c32e5c2f6dc192ee70ef65f9978af36101205161010051055f8112615b97570260c360c051037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8113156129fa5781811b612a00565b81815f031c5b90509050610140526103e96101405110615b9757670de0b6b3a7640000612a286101606126ca565b6101605161014051808202811583838304141715615b97579050905004815250565b61018051606052612a5c6101c0612703565b6101c0516101a052612a6f6101e0612584565b6101e0516101c0526101a0516101c0516fffffffffffffffffffffffffffffffff8111615b97576002810a90506101a0518015615b9757808204905090506101c051808202811583838304141715615b97579050905004815250565b60a05115615b97575f60e05260605115612b1e5760a05160c0516020615cb55f395f51808202811583838304141715615b975790509050606051808202811583838304141715615b9757905090500460e0525b60805115612ba95760e051670de0b6b3a76400006020615c955f395f5160a0516fffffffffffffffffffffffffffffffff8111615b97576002810a9050808202811583838304141715615b97579050905060c0518015615b975780820490509050608051808202811583838304141715615b97579050905004808201828110615b97579050905060e0525b60605115612bbb576080511515612bbd565b5f5b612bfc5760a0516020615c955f395f510260e051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004815250612ce356612ce3565b60e0516fffffffffffffffffffffffffffffffff8111615b97576002810a9050670de0b6b3a76400006020615c955f395f5160021b60a051808202811583838304141715615b975790509050608051808202811583838304141715615b97579050905004606051808202811583838304141715615b975790509050808201828110615b9757905090506101005260a0516020615c955f395f5160011b0260e05161010051604052612cae6101206121ea565b61012051808201828110615b975790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050048152505b565b61018051606052612cf7610200612703565b610200516101e052612d0a610220612584565b61022051610200526101e05115615b97576101a051612de8576101c051612d9d576020615cb55f395f516101e0516101e051610200516fffffffffffffffffffffffffffffffff8111615b97576002810a90500461020051808202811583838304141715615b975790509050046020615c955f395f51808202811583838304141715615b97579050905004815250612f9c565b6101e0516101e051610200516fffffffffffffffffffffffffffffffff8111615b97576002810a90500461020051808202811583838304141715615b97579050905004815250612f9c565b6101c051612e76576020615c955f395f516101e0516020615cb55f395f51808202811583838304141715615b975790509050046101e0526101e051610200516fffffffffffffffffffffffffffffffff8111615b97576002810a90506101e0518015615b97578082049050905061020051808202811583838304141715615b97579050905004815250612f9c565b6101a0516060526101c0516080526102005160a0526101e05160c052612e9d610240612acb565b61024051610220526101e0516020615c955f395f5161022051808202811583838304141715615b97579050905061020051808202811583838304141715615b9757905090500461020051808202811583838304141715615b97579050905061024052610200516020615cb55f395f5161022051808202811583838304141715615b9757905090506101e051808202811583838304141715615b9757905090500461026052610240516101a051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050808201828110615b975790509050610260516101c051808201828110615b9757905090508015615b9757808204905090508152505b565b60126040516020525f5260405f2054606052700100000000000000000000000000000000606051056080526060517001000000000000000000000000000000008107905060a0526f8000000000000000000000000000000060a051126130215770010000000000000000000000000000000060a0510360a0526001608051016080525b60a0518152608051602082015250565b5f60a0526080516060518082038281135f831218615b97579050905060018101818112615b975790505f8112615b9757610700525f6019905b80610720526107005160a051186130805761311b565b60126040516020525f5260405f206001810190506107205160188111615b975781019050546107405260a05160318111615b97576fffffffffffffffffffffffffffffffff61074051168160051b60c001526001810160a052506107005160a051186130eb5761311b565b60a05160318111615b9757610740518060801c90508160051b60c001526001810160a0525060010181811861306a575b505060a05160208160051b0180838260a060045afa50505050565b5f6106c0525f6019905b806106e0526060516106c05110613156576131ea565b6106c051606051811015615b975760051b608001516107005260016106c051016106c0526106c051606051146131ab576106c051606051811015615b975760051b608001518060801b90506107005117610700525b60016106c051016106c0526107005160126040516020525f5260405f206001810190506106e05160188111615b97578101905055600101818118613140575b5050565b600854610240526009546102605261072036610280376007546102e0526102e05160605261321d6109c0612703565b6109c0516109a052600f6102e0516020525f5260405f20546109c05260106102e0516020525f5260405f20546109e0526101a051610a00526002546101e05180828118828411021890509050610a2052600354610a40526032610a60525f610432905b80610a8052608036610aa037610a2051610b20526109c051156132a45760016132ab565b6109e05115155b15613421576032610a6051186132c9576102e0516102c0525f610a60525b6109c0516060526109e0516080526101c05160a0526109a05160c0526132f0610b40612acb565b610b4051610aa052670de0b6b3a76400006020615c955f395f51610aa051808202811583838304141715615b9757905090506101c051808202811583838304141715615b9757905090506109a0518015615b9757808204905090506101c051808202811583838304141715615b97579050905004610ac0526101c0516020615cb55f395f51610aa051808202811583838304141715615b9757905090506109a051808202811583838304141715615b97579050905004610ae052610ac0516109c051808201828110615b975790509050610ae0516109e051808201828110615b975790509050808202811583838304141715615b975790509050610b00526101c0516040526109a051606052613407610b40612467565b610b4051610a205180828118828411021890509050610b20525b610b2051670de0b6b3a763ffff818118670de0b6b3a763ffff831002189050670de0b6b3a7640000036ec097ce7bc90715b34b9f100000000004610b40526032610a6051146134a7576109e051610b60526101805115613484576109c051610b60525b6103005160318111615b9757610b60518160051b61032001526001810161030052505b6101c0516109a051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b605261018051613834576109c0511561378c57610ac0511561378c57610ac051610b005104610ae051808203828111615b9757905090506109e051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba052610a0051610ba051101561363857610ba05160018181186001831102189050610ba052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b8052610a0051610ba051808203828111615b975790509050610a00526109e051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b610320015261028051610ba051808201828110615b975790509050610280526102a0516109c051808201828110615b9757905090506102a052610b805161098051016109805261378c565b610b4051610a0051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b8052610b0051610ae0516109e051610b8051808201828110615b975790509050808201828110615b9757905090508015615b975780820490509050610ac051808203828111615b97579050905060018101818110615b975790506109c0518082811882841002189050905061096052670de0b6b3a7640000610b8051610a005103610a4051808202811583838304141715615b97579050905004610b80526109e051610a0051808201828110615b9757905090506109e0526102a0516109c05161096051808203828111615b975790509050808201828110615b9757905090506102a0526109e051610b8051808203828111615b975790509050610a605161030051811015615b975760051b61032001526101a05161028052610b8051610980510161098052613bbd565b610431610a805114613b9b57610240516102e051186137aa57613bbd565b6031610a6051186137ba57613bbd565b6020615d555f395f51610b605111156137d257613bbd565b6102e05160018103818113615b975790506102e0526020615cb55f395f516109a0516020615c955f395f51808202811583838304141715615b975790509050046109a052600f6102e0516020525f5260405f20546109c0525f6109e052613b9b565b6109e05115613ae657610ae05115613ae657610ae051610b005104610ac051808203828111615b9757905090506109c051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba052610a0051610ba051101561399257610ba05160018181186001831102189050610ba052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b8052610a0051610ba051808203828111615b975790509050610a00526109c051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b610320015261028051610ba051808201828110615b975790509050610280526102a0516109e051808201828110615b9757905090506102a052610b8051610980510161098052613ae6565b610b4051610a0051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b8052610b0051610ac0516109c051610b8051808201828110615b975790509050808201828110615b9757905090508015615b975780820490509050610ae051808203828111615b97579050905060018101818110615b975790506109e0518082811882841002189050905061096052670de0b6b3a7640000610b8051610a005103610a4051808202811583838304141715615b97579050905004610b80526109c051610a0051808201828110615b9757905090506109c0526102a0516109e05161096051808203828111615b975790509050808201828110615b9757905090506102a0526109c051610b8051808203828111615b975790509050610a605161030051811015615b975760051b61032001526101a05161028052610b8051610980510161098052613bbd565b610431610a805114613b9b57610260516102e05118613b0457613bbd565b6031610a605118613b1457613bbd565b6020615d555f395f516ec097ce7bc90715b34b9f100000000004610b60511015613b3d57613bbd565b6102e05160018101818112615b975790506102e0526020615c955f395f516109a0516020615cb55f395f51808202811583838304141715615b975790509050046109a0525f6109c05260106102e0516020525f5260405f20546109e0525b6032610a605114613bb2576001610a605101610a60525b600101818118613280575b5050610200516102005160016102005103610280510104026102805261022051610220516102a05104026102a0526107208161072061028060045afa5050565b600854610240526009546102605261072036610280376007546102e0526102e051606052613c2c6109c0612703565b6109c0516109a052600f6102e0516020525f5260405f20546109c05260106102e0516020525f5260405f20546109e0526101a051610a00526002546101e05180828118828411021890509050610a2052600354610a40526032610a60525f610432905b80610a8052608036610aa037610a2051610b20526109c05115613cb3576001613cba565b6109e05115155b15613e30576032610a605118613cd8576102e0516102c0525f610a60525b6109c0516060526109e0516080526101c05160a0526109a05160c052613cff610b40612acb565b610b4051610aa052670de0b6b3a76400006020615c955f395f51610aa051808202811583838304141715615b9757905090506101c051808202811583838304141715615b9757905090506109a0518015615b9757808204905090506101c051808202811583838304141715615b97579050905004610ac0526101c0516020615cb55f395f51610aa051808202811583838304141715615b9757905090506109a051808202811583838304141715615b97579050905004610ae052610ac0516109c051808201828110615b975790509050610ae0516109e051808201828110615b975790509050808202811583838304141715615b975790509050610b00526101c0516040526109a051606052613e16610b40612467565b610b4051610a205180828118828411021890509050610b20525b610b2051670de0b6b3a763ffff818118670de0b6b3a763ffff831002189050670de0b6b3a7640000036ec097ce7bc90715b34b9f100000000004610b40526032610a605114613eb6576109e051610b60526101805115613e93576109c051610b60525b6103005160318111615b9757610b60518160051b61032001526001810161030052505b6101c0516109a051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b60526101805161420f576109c0511561416757610ac0511561416757610a00516109c051101561403f57610ac051610b005104610ae051808203828111615b9757905090506109e051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b9757905090500460018181186001831102189050610ba052610a00516109c051808203828111615b975790509050610a005261028051610ba051808201828110615b975790509050610280526102a0516109c051808201828110615b9757905090506102a052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109e051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b8051610980510161098052614167565b610a00516109c0510361096052610b0051610ac05161096051808201828110615b9757905090508015615b975780820490509050610ae051808203828111615b9757905090506109e051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba0526101a0516102a05261028051610ba051808201828110615b97579050905061028052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109e051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b8051610980510161098052614564565b610431610a80511461454257610240516102e0511861418557614564565b6031610a60511861419557614564565b6020615d555f395f51610b605111156141ad57614564565b6102e05160018103818113615b975790506102e0526020615cb55f395f516109a0516020615c955f395f51808202811583838304141715615b975790509050046109a052600f6102e0516020525f5260405f20546109c0525f6109e052614542565b6109e0511561448d57610ae0511561448d57610a00516109e051101561436557610ae051610b005104610ac051808203828111615b9757905090506109c051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b9757905090500460018181186001831102189050610ba052610a00516109e051808203828111615b975790509050610a005261028051610ba051808201828110615b975790509050610280526102a0516109e051808201828110615b9757905090506102a052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109c051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b805161098051016109805261448d565b610a00516109e0510361096052610b0051610ae05161096051808201828110615b9757905090508015615b975780820490509050610ac051808203828111615b9757905090506109c051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba0526101a0516102a05261028051610ba051808201828110615b97579050905061028052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109c051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b8051610980510161098052614564565b610431610a80511461454257610260516102e051186144ab57614564565b6031610a6051186144bb57614564565b6020615d555f395f516ec097ce7bc90715b34b9f100000000004610b605110156144e457614564565b6102e05160018101818112615b975790506102e0526020615c955f395f516109a0516020615cb55f395f51808202811583838304141715615b975790509050046109a0525f6109c05260106102e0516020525f5260405f20546109e0525b6032610a605114614559576001610a605101610a60525b600101818118613c8f575b5050610200516102005160016102005103610280510104026102805261022051610220516102a05104026102a0526107208161072061028060045afa5050565b610bc0516145b9576001610be05118156145bb565b5f5b6145da576001610bc051186145d457610be051156145dd565b5f6145dd565b60015b61464657600b610c40527f57726f6e6720696e646578000000000000000000000000000000000000000000610c6052610c4050610c405180610c6001601f825f031636823750506308c379a0610c00526020610c2052601f19601f610c40510116604401610c1cfd5b61072036610c4037610c005161466b5761072081610720610c4060045afa50506147bd565b6020615c55611360396020615c1561138039610bc051614698576020615c15611360396020615c55611380395b6146a36113e0612584565b6113e080516113a05260208101516113c05250610c205161472a57610bc0511561018052610c005161138051808202811583838304141715615b9757905090506101a0526113a0516101c0526113c0516101e052611360516102005261138051610220526147126113e0613bfd565b6113e0610720610c406107208360045afa5050614792565b610bc0511561018052610c005161136051808202811583838304141715615b9757905090506101a0526113a0516101c0526113c0516101e0526113605161020052611380516102205261477e6113e06131ee565b6113e0610720610c406107208360045afa50505b61136051610c405104610c405261138051610c605104610c605261072081610720610c4060045afa50505b565b610bc0516147d4576001610be05118156147d6565b5f5b6147f5576001610bc051186147ef57610be051156147f8565b5f6147f8565b60015b61486157600b610c80527f57726f6e6720696e646578000000000000000000000000000000000000000000610ca052610c8050610c805180610ca001601f825f031636823750506308c379a0610c40526020610c6052601f19601f610c80510116604401610c5cfd5b61486c610cc06125e0565b610cc08051610c80526020810151610ca05250610c0051614896575f81525f602082015250614f4e565b601354610cc0525f610ce0526020615bf5611340396020615c35611360396020615c15611380396020615c556113a0396001610bc051186148f4576113a0516113805261136051611340526020615c156113a0396020615bf5611360395b610720366113c037610c60516149ac577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611ae052611ae051610c0051101561495857610c00516113a051808202811583838304141715615b975790509050611ae0525b610bc0511561018052611ae0516101a052610c80516101c052610ca0516101e05261138051610200526113a05161022052614994611b00613bfd565b611b006107206113c06107208360045afa5050614a14565b610bc0511561018052610c005161138051808202811583838304141715615b9757905090506101a052610c80516101c052610ca0516101e05261138051610200526113a05161022052614a00611ae06131ee565b611ae06107206113c06107208360045afa50505b611380516113c05104611ae0526113a0516113e05104611b0052610c6051614ad257610c2051611ae0511115614a4a575f614a65565b610c0051611b005118614a5e576001614a65565b610c005119155b614b45576008611b20527f536c697070616765000000000000000000000000000000000000000000000000611b4052611b2050611b205180611b4001601f825f031636823750506308c379a0611ae0526020611b0052601f19601f611b20510116604401611afcfd614b45565b610c2051611b00511015614b45576008611b20527f536c697070616765000000000000000000000000000000000000000000000000611b4052611b2050611b205180611b4001601f825f031636823750506308c379a0611ae0526020611b0052601f19601f611b20510116604401611afcfd5b611b0051614b54576001614b5a565b611ae051155b15614b6e575f81525f602082015250614f4e565b61138051611ac05104611ac052610bc051614ba057600a54611ac051808201828110615b975790509050600a55614bb9565b600b54611ac051808201828110615b975790509050600b555b611400516114205180828118828412021890509050611b2052611b2051611b40526114005161142051037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811315614c115780614c1e565b805f038114615b9757805f035b9050611b60525f6032905b80611b8052604036611ba037610bc051614c7257611b805161144051811015615b975760051b6114600151611ba05261142051611b205118614ca857611aa051611bc052614ca8565b611b8051611b60510361144051811015615b975760051b6114600151611bc05261142051611b205118614ca857611aa051611ba0525b611ba051600f611b20516020525f5260405f2055611bc0516010611b20516020525f5260405f2055610cc05115614d42575f611be052611bc05115614d1f576011611b20516020525f5260405f2054611bc051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004611be0525b610ce05160318111615b9757611be0518160051b610d00015260018101610ce052505b611b6051611b805118614d5457614d6a565b6001611b205101611b2052600101818118614c29575b505061142051600755610c40517fb2e76ae99761dc136e598d4a629bb347eccb9532a5f8bbd72e18467c3c34cc98610bc051611b8052611ae051611ba052610be051611bc052611b0051611be0526080611b80a2610cc05115614e5757610cc05163d1c92353611b80526040611b4051611ba05280611bc05280611ba0015f610ce0518083528060051b5f8260328111615b97578015614e2457905b8060051b610d0001518160051b602088010152600101818118614e06575b50508201602001915050905081015050803b15615b97575f611b806106a4611b9c5f855af1614e55573d5f5f3e3d5ffd5b505b611340516323b872dd611b805233611ba05230611bc052611ae051611be0526020611b806064611b9c5f855af1614e90573d5f5f3e3d5ffd5b3d614ea757803b15615b97576001611c0052614ec0565b60203d10615b9757611b80518060011c615b9757611c00525b611c0090505115615b97576113605163a9059cbb611b8052610c4051611ba052611b0051611bc0526020611b806044611b9c5f855af1614f02573d5f5f3e3d5ffd5b3d614f1957803b15615b97576001611be052614f32565b60203d10615b9757611b80518060011c615b9757611be0525b611be090505115615b9757611ae0518152611b00516020820152505b565b61076051604052614f626107e0612f9e565b6107e080516107a05260208101516107c05250610760516040526107a0516060526107c051608052614f95610e40613031565b610e40805160208160051b01806107e0828560045afa505050506107e05115615b97575f60051b6108000151614fce575f81525061588a565b614fd9610e60612584565b610e6051610e4052610e405115615b97576107a05160018103818113615b97579050610e6052600754610e80526107a051606052615018610ec0612703565b610ec051610ea0525f610ec0525f6032905b80610ee052610e605160018101818112615b97579050610e60526107c051610e6051131561505757615854565b604036610f0037610e8051610e605112615080576010610e60516020525f5260405f2054610f20525b610e8051610e6051136150a257600f610e60516020525f5260405f2054610f00525b610ea051610f40526020615c955f395f51610ea0516020615cb55f395f51808202811583838304141715615b97579050905004610ea052610f00516150ee57610f20516150ee57615849565b6011610e60516020525f5260405f2054610f6052610ee0516107e051811015615b975760051b6108000151610f8052610f605161512a57615849565b610f805161513757615849565b610f60516103e88101818110615b97579050610f6052610f4051610e40516fffffffffffffffffffffffffffffffff8111615b97576002810a9050610ea0518015615b975780820490509050610e4051808202811583838304141715615b97579050905004610fa052610f00516151af5760016151b5565b610f2051155b156153a057610f4051610e4051116152b657610ea051610e405110156153a057610f0051610fc052610f005161521057670de0b6b3a7640000610f2051610fa051808202811583838304141715615b97579050905004610fc0525b6107805161525457610ec051610f6051610fc051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f6051610f4051610fc0516020615d155f395f51808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849566153a0565b610f2051610fc052610f20516152fe57610f0051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050610fa0518015615b975780820490509050610fc0525b6107805161536457610ec051610f60516020615d155f395f51610fc051610f4051808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f6051610fc051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610f0051606052610f2051608052610e405160a052610f405160c0526153c7610fe0612acb565b610fe051610fc052670de0b6b3a7640000610f40516020615c955f395f51610fc051808202811583838304141715615b975790509050610e4051808202811583838304141715615b97579050905004610e4051808202811583838304141715615b97579050905004610fe052610e40516020615cb55f395f51610fc051808202811583838304141715615b975790509050610f4051808202811583838304141715615b9757905090500461100052610fe051610f0051808201828110615b97579050905061100051610f2051808201828110615b975790509050808202811583838304141715615b9757905090506110205260403661104037610f4051610e40511161577c57610ea051610e4051106156ab57610e40516020615c955f395f51610fc051808202811583838304141715615b975790509050610ea051610e405103808202811583838304141715615b9757905090500461106052610fe051611020516110005161106051808201828110615b9757905090508015615b975780820490509050610fe0518082811882841102189050905003611040526107805161560457610ec051610f605161104051670de0b6b3a764000061106051610ea051610e4051808202811583838304141715615b9757905090506040526155ad6110806121ea565b61108051808202811583838304141715615b97579050905004808201828110615b975790509050610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f60516110605161104051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050610f4051610e4051808202811583838304141715615b97579050905060405261565c6110806121ea565b611080518015615b975780820490509050808201828110615b975790509050610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610fe05161102051611000518015615b975780820490509050610fe0518082811882841102189050905003611040526107805161571e57610ec051610f605161104051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f6051610f4051611040516020615d155f395f51808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b6110005161102051610fe0518015615b975780820490509050611000518082811882841102189050905003611060526107805161581157610ec051610f60516020615d155f395f5161106051610f4051808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f605161106051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec0525b60010181811861502a575b505061078051615878576020615c155f395f51610ec0510481525061588a5661588a565b6020615c555f395f51610ec051048152505b565b5f6107a0525f610e005261078051156158de576107a05160318111615b97575f8160051b6107c00152600181016107a05250610e005160318111615b97575f8160051b610e20015260018101610e0052505b610760516040526158f06114a0612f9e565b6114a080516114605260208101516114805250610760516040526114605160605261148051608052615923611b00613031565b611b00805160208160051b01806114a0828560045afa505050506114a05115615b97575f60051b6114c0015115615af8575f6032905b80611b00526011611460516020525f5260405f20546103e88101818110615b97579050611b2052611b00516114a051811015615b975760051b6114c00151611b4052611b2051600f611460516020525f5260405f205460018101818110615b97579050611b4051808202811583838304141715615b97579050905004611b6052611b20516010611460516020525f5260405f205460018101818110615b97579050611b4051808202811583838304141715615b97579050905004611b805261078051615a7c576107a05160318111615b97576020615c155f395f51611b6051048160051b6107c00152600181016107a05250610e005160318111615b97576020615c555f395f51611b8051048160051b610e20015260018101610e005250615acd565b6107a05115615b97575f60051b6107c0018051611b6051808201828110615b975790509050815250610e005115615b97575f60051b610e20018051611b8051808201828110615b9757905090508152505b611480516114605118615adf57615af5565b6001611460510161146052600101818118615959575b50505b6107805115615b5e576020615c155f395f516107a05115615b97575f60051b6107c00151046107a05115615b97575f60051b6107c001526020615c555f395f51610e005115615b97575f60051b610e20015104610e005115615b97575f60051b610e2001525b6107a05160208160051b018083826107a060045afa505050610e005160208160051b016106608301818183610e0060045afa5050505050565b5f80fd216010d9083308c0216021600aa71e8a21600f2d21600efb08a0216021600b492160212c21600b18205f0ebc09cb0c0220c5216021600e98007821600ac70ae7001817c9106a1e250d432160216009eb00381f4c0de80c9c1eea84195bf581185a1901a0a16576797065728300030a0017000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd520000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000e1d72befab679d60000000000000000000000000000000000000000000000000078714818f6aa3b0000000000000000000000000000000000000000000000000b8dc3b54c82feb400000000000000000000000000000000000000000000000000071afd498d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0a4c53408f5acf3246c83b9b8bd8d36d5ee38b8

Deployed Bytecode

0x5f3560e01c6002602d820660011b615b9b01601e395f51565b63f851a44081186121605734615b975760015460405260206040f3612160565b63f446c1d081186100565734615b97576020615c9560403960206040f35b635ea0e01b81186121605734615b97576020615d7560403960206040f3612160565b63ddca3f4381186100945734615b975760025460405260206040f35b633c10269a81186100f15760a436103417615b97576084358060a01c615b9757612240525b5f54600214615b975760025f55604060806004610bc03761224051610c40525f610c60526100e86122606147bf565b61226060035f55f35b6348e995f9811861216057602436103417615b97575f54600214615b9757600854610280526009546102a0526007546102c05261012f610320612584565b61032080516102e052602081015161030052506102c051606052610154610340612703565b610340516103205261032051610320516102e0516fffffffffffffffffffffffffffffffff8111615b97576002810a9050046102e051808202811583838304141715615b97579050905004610340526020615cf55f395f51610340516020615cd55f395f51808202811583838304141715615b975790509050046103605260a036610380376032610420526001610440525f610432905b80610460526103205115615b9757600f6102c0516020525f5260405f20546104805260106102c0516020525f5260405f20546104a0526104605161025f576102c05161018052610480516101a0526104a0516101c05261024c6104c0612ce5565b6104c051600435101561025f575f610440525b610480511561026f576001610276565b6104a05115155b6104c0526104c051156103ad57610480516060526104a0516080526102e05160a0526103205160c0526102aa6104e0612acb565b6104e0516103a052670de0b6b3a7640000610320516020615c955f395f516103a051808202811583838304141715615b9757905090506102e051808202811583838304141715615b975790509050046102e051808202811583838304141715615b975790509050046103c0526102e0516020615cb55f395f516103a051808202811583838304141715615b97579050905061032051808202811583838304141715615b975790509050046103e0526103c05161048051808201828110615b9757905090506103e0516104a051808201828110615b975790509050808202811583838304141715615b97579050905061040052603261042051186103ad575f610420525b61036051600435116104df5761034051600435106104df576104c05115610767576103e05161040051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790506004358015615b9757808204905090506040526104126105006121ea565b610500516103e05180828118828411021890509050036104e0526103c051610400516103e0516104e051808201828110615b9757905090508015615b9757808204905090506103c051808281188284110218905090500361050052610440516104aa57610380516104a0516104e0516104a0518082811882841102189050905003808201828110615b97579050905061038052610767565b610380516104805161050051610480518082811882841102189050905003808201828110615b97579050905061038052610767565b6102e05161032051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050046104e05261044051610625576104c0511561056b5761038051610400516103c0518015615b9757808204905090506103e051808203828111615b9757905090506104a051808203828111615b975790509050808201828110615b975790509050610380525b610280516102c0511861057d57610767565b6031610420511861058d57610767565b6020615d555f395f516104e05111156105a557610767565b6102c05160018103818113615b975790506102c05261034051610360526020615cd55f395f51610340516020615cf55f395f51808202811583838304141715615b97579050905004610340526020615cb55f395f51610320516020615c955f395f51808202811583838304141715615b9757905090500461032052610745565b6104c0511561067e5761038051610400516103e0518015615b9757808204905090506103c051808203828111615b97579050905061048051808203828111615b975790509050808201828110615b975790509050610380525b6102a0516102c0511861069057610767565b603161042051186106a057610767565b6020615d555f395f516ec097ce7bc90715b34b9f1000000000046104e05110156106c957610767565b6102c05160018101818112615b975790506102c05261036051610340526020615cf55f395f51610360516020615cd55f395f51808202811583838304141715615b97579050905004610360526020615c955f395f51610320516020615cb55f395f51808202811583838304141715615b97579050905004610320525b6032610420511461075c5760016104205101610420525b6001018181186101eb575b505061038051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790506002546103005180828118828411021890509050670de0b6b3a7640000038015615b97578082049050905061038052610380516107d9575f610460526104405161048052604061046061082d565b610440516107fe5760016020615c555f395f5160016103805103040161038052610817565b60016020615c155f395f51600161038051030401610380525b6103805161046052610440516104805260406104605bf3612160565b63fee3f7f9811861084f5734615b975760035460405260206040f35b63ee4c32ee811861216057602436103417615b97576004358060a01c615b97576114a0525f54600214615b975760206114a051610760526001610780526108976114c0614f50565b6114c0f3612160565b632c4e722e81186121605734615b975760045460405260206040f3612160565b638f8654c581186108dc5734615b975760075460405260206040f35b63ed7110cf811861093c57606436103417615b97575f54600214615b975760606004610bc0375f610c20526109126122206145a4565b612220610720611b006107208360045afa5050611b205161222052611b0051612240526040612220f35b63d4387a99811861216057602436103417615b97575f54600214615b975760025f556001543318615b9757610971606061265f565b606051604052604051600655426005556004356004557f52543716810f73c3fa9bca74622aecb6d3614ca4991472f3e999d531c2f6afb86004356060526040516080524260a05260606060a16020604060035f55f3612160565b63ca72a82181186121605734615b975760085460405260206040f3612160565b63aaa615fc8118610a075734615b975760095460405260206040f35b6324299b7a8118610a4157602436103417615b9757602060043560018101818112615b97579050606052610a3c610180612703565b610180f35b63f2388acb81186121605734615b97575f54600214615b97576007546102805260206102805161018052600f610280516020525f5260405f20546101a0526010610280516020525f5260405f20546101c052610a9e6102a0612ce5565b6102a0f3612160565b63d1fea73381186121605734615b9757600a5460405260206040f3612160565b6389960ba781186121605734615b9757600b5460405260206040f3612160565b63ebcb0067811861216057602436103417615b9757600f6004356020525f5260405f205460405260206040f3612160565b6331f7e306811861216057602436103417615b975760106004356020525f5260405f205460405260206040f3612160565b63611105d38118610b655734615b975760135460405260206040f35b63c16ef26481186121605734615b97575f54600214615b97576007546040526040516060526008546080525f610400905b8060a0526080516060511215610bbf576040516104008103818113615b97579050606052610bf6565b600f6060516020525f5260405f205415610bd857610bf6565b60605160018103818113615b97579050606052600101818118610b96575b505060206060f3612160565b63e9333fab8118610c6457602436103417615b97576004358060a01c615b975761010052600154615b9757610100516001556020615bf560403961010051606052610c4b612164565b6020615c3560403961010051606052610c62612164565b005b63822fe50781186121605734615b97575f54600214615b975760025f556001543318615b97575f600a555f600b5560035f5500612160565b63c66106578118610cd957602436103417615b975760206020615bf56040396020615c35606039604060043560018111615b975760051b81019050f35b6386fc88d38118610cfb5734615b97576020610cf6610180612584565b610180f35b63556d6e9f811861216057606436103417615b97575f54600214615b9757602060606004610bc0376001610c2052610d34611b006145a4565b611b00602081019050f3612160565b6377c345948118610d835734615b9757600254610d61610180612584565b61018060208101905051808281188284110218905090506101c05260206101c0f35b63b461100d8118610dc557602436103417615b97576004358060a01c615b975760c0525f54600214615b9757604060c051604052610dc160e0612f9e565b60e0f35b63a3e346ec811861216057608436103417615b975733612240526100b956612160565b63095a0fc68118610e085734615b97576020610e04604061265f565b6040f35b63544fb5c1811861216057602436103417615b97576004358060a01c615b9757612160525f54600214615b97576121605161076052600161078052610e4e612e4061588c565b612e40610cc0612180610cc08360045afa50506121805115615b97575f60051b6121a00151612e40526127e05115615b97575f60051b6128000151612e60526040612e40f3612160565b63a7db79a581186121605734615b97576020610eb460606126ca565b6060f3612160565b637c1bbd83811861216057602436103417615b9757602060043560018101818112615b9757905061018052610ef2610220612a4a565b610220f3612160565b63c32bd03c811861216057602436103417615b9757602060043561018052610f24610220612a4a565b610220f3612160565b632eb858e78118610f5a57602436103417615b97576020600435606052610f55610180612703565b610180f35b63ec654706811861101a57602436103417615b97575f54600214615b97576007546040525f610400905b8060605260405160043513610fc457600f6040516020525f5260405f205415610fb65750505f60805260206080611018565b600160405103604052610ff1565b60106040516020525f5260405f205415610fe75750505f60805260206080611018565b6001604051016040525b600435604051186110015761100c565b600101818118610f84575b50506001606052602060605bf35b6362ca4b18811861216057602436103417615b97576004358060a01c615b97576114a0525f54600214615b975760206114a051610760525f610780526110616114c0614f50565b6114c0f3612160565b63e8dd1ef181186110b657602436103417615b97576004358060a01c615b97576040525f54600214615b975760126040516020525f5260405f2060018101905054151560605260206060f35b635b41b908811861216057608436103417615b97573361224052611f0f56612160565b63ab047e00811460033611161561216057608436103417615b97576004358060a01c615b9757610720525f54600214615b975760025f556001543318615b97575f610740525f610da052600754611400526f7fffffffffffffffffffffffffffffff60643513615b97577fffffffffffffffffffffffffffffffff8000000000000000000000000000000160443512615b97576001604435606435035f8112615b9757016114205260326114205111615b9757611420516024356020615c555f395f51808202811583838304141715615b9757905090500461144052606561144051101561122657600e611460527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006114805261146050611460518061148001601f825f031636823750506308c379a061142052602061144052601f19601f61146051011660440161143cfd5b6012610720516020525f5260405f2060018101905054615b975760643560801b604435016012610720516020525f5260405f2055601354611460525f610401905b806114805261140051604435131561128e57611480511561133b576114005160075561133b565b600f611400516020525f5260405f20546112b0576103ff6114805113156112b2565b5f5b61131b57601a6114a0527f4465706f7369742062656c6f772063757272656e742062616e640000000000006114c0526114a0506114a051806114c001601f825f031636823750506308c379a061146052602061148052601f19601f6114a051011660440161147cfd5b6114005160018103818113615b9757905061140052600101818118611267575b50505f6032905b806114805261148051604435016114a0526064356114a051131561136557611608565b600f6114a0516020525f5260405f2054156113df57600e6114c0527f42616e64206e6f7420656d7074790000000000000000000000000000000000006114e0526114c0506114c051806114e001601f825f031636823750506308c379a06114805260206114a052601f19601f6114c051011660440161149cfd5b611440516114c05261148051611441576024356020615c555f395f51808202811583838304141715615b9757905090506114c05160016114205103808202811583838304141715615b975790509050808203828111615b9757905090506114c0525b60106114a0516020525f5260405f20546114e05260116114a0516020525f5260405f2054611500526114e05160018101818110615b97579050611500516103e88101818110615b975790506114c051808202811583838304141715615b97579050905004611520526115205161151657600e611540527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006115605261154050611540518061156001601f825f031636823750506308c379a061150052602061152052601f19601f61154051011660440161151cfd5b6107405160318111615b9757611520518160051b61076001526001810161074052506115005161152051808201828110615b975790509050611500526fffffffffffffffffffffffffffffffff6115005111615b97576115005160116114a0516020525f5260405f20556114e0516114c051808201828110615b9757905090506114e0526114e05160106114a0516020525f5260405f205561146051156115fd57610da05160318111615b9757611500516114e051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050048160051b610dc0015260018101610da052505b600101818118611342575b50506008546044358082811882841202189050905060085560095460643580828118828413021890509050600955610720516040526107405160208160051b018060608261074060045afa50505061165e613136565b610720517f7e4f5fadb3361b33669433b392d1a203b7a236710eb272650052592e6ce62f0960606024611480376060611480a261146051156117bf576114605163d1c923536114805260406044356114a052806114c052806114a0015f610da0518083528060051b5f8260328111615b975780156116f657905b8060051b610dc001518160051b6020880101526001018181186116d8575b50508201602001915050905081015050803b15615b97575f6114806106a461149c5f855af1611727573d5f5f3e3d5ffd5b506114605163f9d0ca12611480526060610720516114a0526044356114c052806114e052806114a0015f610740518083528060051b5f8260328111615b9757801561178c57905b8060051b61076001518160051b60208801015260010181811861176e575b50508201602001915050905081015050803b15615b97575f6114806106c461149c5f855af16117bd573d5f5f3e3d5ffd5b505b60035f5500612160565b63f3fef3a3811861216057604436103417615b97576004358060a01c615b9757610760525f54600214615b975760025f556001543318615b9757670de0b6b3a764000060243511615b9757601354610780526107605160405261182d6107e0612f9e565b6107e080516107a05260208101516107c052506107a0516107e052610760516040526107a0516060526107c051608052611868610e60613031565b610e60805160208160051b0180610800828560045afa505050506108005115615b97575f60051b61082001516118fd57600b610e60527f4e6f206465706f73697473000000000000000000000000000000000000000000610e8052610e6050610e605180610e8001601f825f031636823750506308c379a0610e20526020610e4052601f19601f610e60510116604401610e3cfd5b604036610e6037600854610ea052610ea051610ec052600954610ee0526107e05160018103818113615b97579050610f00525f6032905b80610f2052600f6107e0516020525f5260405f2054610f405260106107e0516020525f5260405f2054610f6052670de0b6b3a7640000602435610f205161080051811015615b975760051b6108200151808202811583838304141715615b97579050905004610f8052610f8051610f205161080051811015615b975760051b610820015103610f205161080051811015615b975760051b610820015260116107e0516020525f5260405f2054610fa052610fa051610f8051808203828111615b975790509050610fc052610fc05160116107e0516020525f5260405f2055610fa0516103e88101818110615b97579050610fa052610fa051610f405160018101818110615b97579050610f8051808202811583838304141715615b97579050905004610fe052610fa051610f605160018101818110615b97579050610f8051808202811583838304141715615b9757905090500461100052610f4051610fe051808203828111615b975790509050610f4052610f605161100051808203828111615b975790509050610f6052610fc051611b2857610f405115611af457600a546020615c155f395f51610f405104808201828110615b975790509050600a555b610f605115611b2057600b546020615c555f395f51610f605104808201828110615b975790509050600b555b604036610f40375b610ea0516107e05118611b5b57610f4051611b5b57610f6051611b5b57610ea05160018101818112615b97579050610ea0525b610f405115611b6b576001611b72565b610f605115155b15611b80576107e051610f00525b610f4051600f6107e0516020525f5260405f2055610f605160106107e0516020525f5260405f2055610e6051610fe051808201828110615b975790509050610e6052610e805161100051808201828110615b975790509050610e80526107c0516107e05118611bf257611c0956611bfe565b60016107e051016107e0525b600101818118611934575b5050670de0b6b3a764000060243518611c38575f6012610760516020525f5260405f2060018101905055611c60565b610760516040526108005160208160051b018060608261080060045afa505050611c60613136565b610ea051610ec05114611c7557610ea0516008555b6107c051610ee05113611c8a57610f00516009555b6020615c155f395f51610e605104610e60526020615c555f395f51610e805104610e8052610760517ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568610e6051610f2052610e8051610f40526040610f20a26107805115611e06576107805163d1c92353610f205260405f610f405280610f605280610f40015f5f82525f5f5f60328111615b97578015611d3d57905b5f8160051b602087010152600101818118611d27575b505081016020019050905081015050803b15615b97575f610f206106a4610f3c5f855af1611d6d573d5f5f3e3d5ffd5b506107805163f9d0ca12610f2052606061076051610f40526107a051610f605280610f805280610f40015f610800518083528060051b5f8260328111615b97578015611dd357905b8060051b61082001518160051b602088010152600101818118611db5575b50508201602001915050905081015050803b15615b97575f610f206106c4610f3c5f855af1611e04573d5f5f3e3d5ffd5b505b610e6051610f2052610e8051610f40526040610f2060035f55f3612160565b63c49202e7811861216057606436103417615b97575f54600214615b975760606004610bc0376001610c2052611e5c6122206145a4565b612220610720611b006107208360045afa5050611b005161222052611b2051612240526040612220f3612160565b6337ed3a7a811861216057606436103417615b97575f54600214615b975760606004610bc0375f610c2052611ec06122206145a4565b612220610720611b006107208360045afa5050604435611b205118615b97576020611b00f3612160565b63a64833a081186121605760a436103417615b97576084358060a01c615b9757612240525b5f54600214615b975760025f55604060806004610bc03761224051610c40526001610c6052611f3f6122606147bf565b61226060035f55f3612160565b6384738380811861216057602436103417615b97576004358060a01c615b9757612160525f54600214615b9757602080612e405261216051610760525f61078052611f9861218061588c565b61218081612e400160408082528082015f84518083528060051b5f8260328111615b97578015611fe357905b8060051b60208a0101518160051b602088010152600101818118611fc4575b5050820160200191505090508101905080602083015261066083018183015f82518083528060051b5f8260328111615b9757801561203c57905b8060051b6020880101518160051b60208801015260010181811861201d575b5050820160200191505090509050810190509050905081019050612e40f3612160565b631aa02d59811861216057602436103417615b97575f54600214615b975760025f556001543318615b97576004356002557e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a760043560405260206040a160035f5500612160565b633217902f811861216057602436103417615b97575f54600214615b975760025f556001543318615b97576004356003557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b7460043560405260206040a160035f5500612160565b63cc1891c7811861216057602436103417615b97576004358060a01c615b97576040526001543318615b9757604051601355005b5f5ffd5b60405163095ea7b360805260605160a0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60c052602060806044609c5f855af16121b1573d5f5f3e3d5ffd5b3d6121c757803b15615b9757600160e0526121de565b60203d10615b97576080518060011c615b975760e0525b60e090505115615b9757565b6040518060b5710100000000000000000000000000000000008210612216578160801c91508060401b90505b69010000000000000000008210612234578160401c91508060201b90505b65010000000000821061224e578160201c91508060101b90505b63010000008210612266578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050815250565b60405160605242600e54808203828111615b975790509050806078811882607810021890506078036080525f60a0526080511561245757600c5460c052600d5460e05260c051604051116123985760c051604051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790500460a052670b1a2bc2ec4fffff60a0511161240c57671158e460913d000060c051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004606052670b1a2bc2ec50000060a05261240c565b60405160c051670de0b6b3a7640000810281670de0b6b3a7640000820418615b975790500460a052670b1a2bc2ec4fffff60a0511161240c57670de0b6b3a764000060c051671158e460913d0000810281671158e460913d0000820418615b9757905004606052670b1a2bc2ec50000060a0525b60786080516ec097ce7bc90715b34b9f1000000000600360a0510a0460e051670de0b6b3a764000001030204670de0b6b3a763ffff818118670de0b6b3a763ffff83100218905060a0525b606051815260a051602082015250565b6060516060516040516fffffffffffffffffffffffffffffffff8111615b97576002810a905004604051808202811583838304141715615b975790509050046080526020615cb55f395f516020615cb55f395f516080516020615c955f395f51808202811583838304141715615b975790509050046020615c955f395f51808202811583838304141715615b9757905090500460a052608051604051106125555760a05160405111612520575f81525061258256612582565b60405160a051604051036703782dace9d900008102816703782dace9d90000820418615b975790500481525061258256612582565b608051604051608051036703782dace9d900008102816703782dace9d90000820418615b97579050048152505b565b6020615d755f395f5163a035b1fe610100526020610100600461011c845afa6125af573d5f5f3e3d5ffd5b60203d10615b97576101009050516040526125cb6101406122d2565b61014080518252602081015160208301525050565b6020615d755f395f5163ceb7f759610140526020610140600461015c5f855af161260c573d5f5f3e3d5ffd5b60203d10615b97576101409050516040526126286101806122d2565b6101808051610100526020810151610120525042600e5561010051600c5561012051600d5561010051815261012051602082015250565b670de0b6b3a764000060065460045442600554808203828111615b975790509050808202811583838304141715615b97579050905080670de0b6b3a764000001670de0b6b3a76400008110615b97579050808202811583838304141715615b97579050905004815250565b670de0b6b3a76400006020615c755f395f516126e6604061265f565b604051808202811583838304141715615b97579050905004815250565b6060517f80000000000000000000000000000000000000000000000000000000000000008114615b97575f036020615d355f395f5180820281191515600160ff1b8414151782158484840514171615615b9757905090506080527ffffffffffffffffffffffffffffffffffffffffffffffffdc0d0570925a4668160805112615b9757680755bf798b4a1bf1e460805113615b9757670de0b6b3a764000060805160601b0560a0526c010000000000000000000000006b8000000000000000000000006bb17217f7d1cf79abc9e3b39860a05160601b05010560c0526bb17217f7d1cf79abc9e3b39860c0510260a0510360a0526c10fe68e7fd37d0007b713f765060a0510160e0526d02d16720577bd19bf614176fe9ea6c0100000000000000000000000060a05160e05102050160e0526d04a4fd9f2a8b96949216d2255a6c60a05160e0510103610100526e0587f503bb6ea29d25fcb7401964506c0100000000000000000000000060e051610100510205016101005279d835ebba824c98fb31b83b2ca45c00000000000000000000000060a0516101005102016101005260a0516c240c330e9fb2d9cbaf0fd5aafc8103818113615b97579050610120526d0277594991cfc85f6e2461837cd96c0100000000000000000000000060a05161012051020501610120526d1a521255e34f6a5061b25ef1c9c46c0100000000000000000000000060a05161012051020503610120526db1bbb201f443cf962f1a1d3db4a56c0100000000000000000000000060a05161012051020501610120526e02c72388d9f74f51a9331fed693f156c0100000000000000000000000060a05161012051020503610120526e05180bb14799ab47a8a8cb2a527d576c0100000000000000000000000060a051610120510205016101205274029d9dc38563c32e5c2f6dc192ee70ef65f9978af36101205161010051055f8112615b97570260c360c051037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8113156129fa5781811b612a00565b81815f031c5b90509050610140526103e96101405110615b9757670de0b6b3a7640000612a286101606126ca565b6101605161014051808202811583838304141715615b97579050905004815250565b61018051606052612a5c6101c0612703565b6101c0516101a052612a6f6101e0612584565b6101e0516101c0526101a0516101c0516fffffffffffffffffffffffffffffffff8111615b97576002810a90506101a0518015615b9757808204905090506101c051808202811583838304141715615b97579050905004815250565b60a05115615b97575f60e05260605115612b1e5760a05160c0516020615cb55f395f51808202811583838304141715615b975790509050606051808202811583838304141715615b9757905090500460e0525b60805115612ba95760e051670de0b6b3a76400006020615c955f395f5160a0516fffffffffffffffffffffffffffffffff8111615b97576002810a9050808202811583838304141715615b97579050905060c0518015615b975780820490509050608051808202811583838304141715615b97579050905004808201828110615b97579050905060e0525b60605115612bbb576080511515612bbd565b5f5b612bfc5760a0516020615c955f395f510260e051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004815250612ce356612ce3565b60e0516fffffffffffffffffffffffffffffffff8111615b97576002810a9050670de0b6b3a76400006020615c955f395f5160021b60a051808202811583838304141715615b975790509050608051808202811583838304141715615b97579050905004606051808202811583838304141715615b975790509050808201828110615b9757905090506101005260a0516020615c955f395f5160011b0260e05161010051604052612cae6101206121ea565b61012051808201828110615b975790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050048152505b565b61018051606052612cf7610200612703565b610200516101e052612d0a610220612584565b61022051610200526101e05115615b97576101a051612de8576101c051612d9d576020615cb55f395f516101e0516101e051610200516fffffffffffffffffffffffffffffffff8111615b97576002810a90500461020051808202811583838304141715615b975790509050046020615c955f395f51808202811583838304141715615b97579050905004815250612f9c565b6101e0516101e051610200516fffffffffffffffffffffffffffffffff8111615b97576002810a90500461020051808202811583838304141715615b97579050905004815250612f9c565b6101c051612e76576020615c955f395f516101e0516020615cb55f395f51808202811583838304141715615b975790509050046101e0526101e051610200516fffffffffffffffffffffffffffffffff8111615b97576002810a90506101e0518015615b97578082049050905061020051808202811583838304141715615b97579050905004815250612f9c565b6101a0516060526101c0516080526102005160a0526101e05160c052612e9d610240612acb565b61024051610220526101e0516020615c955f395f5161022051808202811583838304141715615b97579050905061020051808202811583838304141715615b9757905090500461020051808202811583838304141715615b97579050905061024052610200516020615cb55f395f5161022051808202811583838304141715615b9757905090506101e051808202811583838304141715615b9757905090500461026052610240516101a051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050808201828110615b975790509050610260516101c051808201828110615b9757905090508015615b9757808204905090508152505b565b60126040516020525f5260405f2054606052700100000000000000000000000000000000606051056080526060517001000000000000000000000000000000008107905060a0526f8000000000000000000000000000000060a051126130215770010000000000000000000000000000000060a0510360a0526001608051016080525b60a0518152608051602082015250565b5f60a0526080516060518082038281135f831218615b97579050905060018101818112615b975790505f8112615b9757610700525f6019905b80610720526107005160a051186130805761311b565b60126040516020525f5260405f206001810190506107205160188111615b975781019050546107405260a05160318111615b97576fffffffffffffffffffffffffffffffff61074051168160051b60c001526001810160a052506107005160a051186130eb5761311b565b60a05160318111615b9757610740518060801c90508160051b60c001526001810160a0525060010181811861306a575b505060a05160208160051b0180838260a060045afa50505050565b5f6106c0525f6019905b806106e0526060516106c05110613156576131ea565b6106c051606051811015615b975760051b608001516107005260016106c051016106c0526106c051606051146131ab576106c051606051811015615b975760051b608001518060801b90506107005117610700525b60016106c051016106c0526107005160126040516020525f5260405f206001810190506106e05160188111615b97578101905055600101818118613140575b5050565b600854610240526009546102605261072036610280376007546102e0526102e05160605261321d6109c0612703565b6109c0516109a052600f6102e0516020525f5260405f20546109c05260106102e0516020525f5260405f20546109e0526101a051610a00526002546101e05180828118828411021890509050610a2052600354610a40526032610a60525f610432905b80610a8052608036610aa037610a2051610b20526109c051156132a45760016132ab565b6109e05115155b15613421576032610a6051186132c9576102e0516102c0525f610a60525b6109c0516060526109e0516080526101c05160a0526109a05160c0526132f0610b40612acb565b610b4051610aa052670de0b6b3a76400006020615c955f395f51610aa051808202811583838304141715615b9757905090506101c051808202811583838304141715615b9757905090506109a0518015615b9757808204905090506101c051808202811583838304141715615b97579050905004610ac0526101c0516020615cb55f395f51610aa051808202811583838304141715615b9757905090506109a051808202811583838304141715615b97579050905004610ae052610ac0516109c051808201828110615b975790509050610ae0516109e051808201828110615b975790509050808202811583838304141715615b975790509050610b00526101c0516040526109a051606052613407610b40612467565b610b4051610a205180828118828411021890509050610b20525b610b2051670de0b6b3a763ffff818118670de0b6b3a763ffff831002189050670de0b6b3a7640000036ec097ce7bc90715b34b9f100000000004610b40526032610a6051146134a7576109e051610b60526101805115613484576109c051610b60525b6103005160318111615b9757610b60518160051b61032001526001810161030052505b6101c0516109a051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b605261018051613834576109c0511561378c57610ac0511561378c57610ac051610b005104610ae051808203828111615b9757905090506109e051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba052610a0051610ba051101561363857610ba05160018181186001831102189050610ba052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b8052610a0051610ba051808203828111615b975790509050610a00526109e051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b610320015261028051610ba051808201828110615b975790509050610280526102a0516109c051808201828110615b9757905090506102a052610b805161098051016109805261378c565b610b4051610a0051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b8052610b0051610ae0516109e051610b8051808201828110615b975790509050808201828110615b9757905090508015615b975780820490509050610ac051808203828111615b97579050905060018101818110615b975790506109c0518082811882841002189050905061096052670de0b6b3a7640000610b8051610a005103610a4051808202811583838304141715615b97579050905004610b80526109e051610a0051808201828110615b9757905090506109e0526102a0516109c05161096051808203828111615b975790509050808201828110615b9757905090506102a0526109e051610b8051808203828111615b975790509050610a605161030051811015615b975760051b61032001526101a05161028052610b8051610980510161098052613bbd565b610431610a805114613b9b57610240516102e051186137aa57613bbd565b6031610a6051186137ba57613bbd565b6020615d555f395f51610b605111156137d257613bbd565b6102e05160018103818113615b975790506102e0526020615cb55f395f516109a0516020615c955f395f51808202811583838304141715615b975790509050046109a052600f6102e0516020525f5260405f20546109c0525f6109e052613b9b565b6109e05115613ae657610ae05115613ae657610ae051610b005104610ac051808203828111615b9757905090506109c051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba052610a0051610ba051101561399257610ba05160018181186001831102189050610ba052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b8052610a0051610ba051808203828111615b975790509050610a00526109c051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b610320015261028051610ba051808201828110615b975790509050610280526102a0516109e051808201828110615b9757905090506102a052610b8051610980510161098052613ae6565b610b4051610a0051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b8052610b0051610ac0516109c051610b8051808201828110615b975790509050808201828110615b9757905090508015615b975780820490509050610ae051808203828111615b97579050905060018101818110615b975790506109e0518082811882841002189050905061096052670de0b6b3a7640000610b8051610a005103610a4051808202811583838304141715615b97579050905004610b80526109c051610a0051808201828110615b9757905090506109c0526102a0516109e05161096051808203828111615b975790509050808201828110615b9757905090506102a0526109c051610b8051808203828111615b975790509050610a605161030051811015615b975760051b61032001526101a05161028052610b8051610980510161098052613bbd565b610431610a805114613b9b57610260516102e05118613b0457613bbd565b6031610a605118613b1457613bbd565b6020615d555f395f516ec097ce7bc90715b34b9f100000000004610b60511015613b3d57613bbd565b6102e05160018101818112615b975790506102e0526020615c955f395f516109a0516020615cb55f395f51808202811583838304141715615b975790509050046109a0525f6109c05260106102e0516020525f5260405f20546109e0525b6032610a605114613bb2576001610a605101610a60525b600101818118613280575b5050610200516102005160016102005103610280510104026102805261022051610220516102a05104026102a0526107208161072061028060045afa5050565b600854610240526009546102605261072036610280376007546102e0526102e051606052613c2c6109c0612703565b6109c0516109a052600f6102e0516020525f5260405f20546109c05260106102e0516020525f5260405f20546109e0526101a051610a00526002546101e05180828118828411021890509050610a2052600354610a40526032610a60525f610432905b80610a8052608036610aa037610a2051610b20526109c05115613cb3576001613cba565b6109e05115155b15613e30576032610a605118613cd8576102e0516102c0525f610a60525b6109c0516060526109e0516080526101c05160a0526109a05160c052613cff610b40612acb565b610b4051610aa052670de0b6b3a76400006020615c955f395f51610aa051808202811583838304141715615b9757905090506101c051808202811583838304141715615b9757905090506109a0518015615b9757808204905090506101c051808202811583838304141715615b97579050905004610ac0526101c0516020615cb55f395f51610aa051808202811583838304141715615b9757905090506109a051808202811583838304141715615b97579050905004610ae052610ac0516109c051808201828110615b975790509050610ae0516109e051808201828110615b975790509050808202811583838304141715615b975790509050610b00526101c0516040526109a051606052613e16610b40612467565b610b4051610a205180828118828411021890509050610b20525b610b2051670de0b6b3a763ffff818118670de0b6b3a763ffff831002189050670de0b6b3a7640000036ec097ce7bc90715b34b9f100000000004610b40526032610a605114613eb6576109e051610b60526101805115613e93576109c051610b60525b6103005160318111615b9757610b60518160051b61032001526001810161030052505b6101c0516109a051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004610b60526101805161420f576109c0511561416757610ac0511561416757610a00516109c051101561403f57610ac051610b005104610ae051808203828111615b9757905090506109e051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b9757905090500460018181186001831102189050610ba052610a00516109c051808203828111615b975790509050610a005261028051610ba051808201828110615b975790509050610280526102a0516109c051808201828110615b9757905090506102a052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109e051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b8051610980510161098052614167565b610a00516109c0510361096052610b0051610ac05161096051808201828110615b9757905090508015615b975780820490509050610ae051808203828111615b9757905090506109e051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba0526101a0516102a05261028051610ba051808201828110615b97579050905061028052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109e051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b8051610980510161098052614564565b610431610a80511461454257610240516102e0511861418557614564565b6031610a60511861419557614564565b6020615d555f395f51610b605111156141ad57614564565b6102e05160018103818113615b975790506102e0526020615cb55f395f516109a0516020615c955f395f51808202811583838304141715615b975790509050046109a052600f6102e0516020525f5260405f20546109c0525f6109e052614542565b6109e0511561448d57610ae0511561448d57610a00516109e051101561436557610ae051610b005104610ac051808203828111615b9757905090506109c051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b9757905090500460018181186001831102189050610ba052610a00516109e051808203828111615b975790509050610a005261028051610ba051808201828110615b975790509050610280526102a0516109e051808201828110615b9757905090506102a052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109c051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b805161098051016109805261448d565b610a00516109e0510361096052610b0051610ae05161096051808201828110615b9757905090508015615b975780820490509050610ac051808203828111615b9757905090506109c051808203828111615b975790509050610b8052670de0b6b3a7640000610b8051610b4051808202811583838304141715615b97579050905004610ba0526101a0516102a05261028051610ba051808201828110615b97579050905061028052670de0b6b3a7640000610b8051610ba05103610a4051808202811583838304141715615b97579050905004610b80526109c051610ba051808201828110615b975790509050610b8051808203828111615b975790509050610a605161030051811015615b975760051b6103200152610b8051610980510161098052614564565b610431610a80511461454257610260516102e051186144ab57614564565b6031610a6051186144bb57614564565b6020615d555f395f516ec097ce7bc90715b34b9f100000000004610b605110156144e457614564565b6102e05160018101818112615b975790506102e0526020615c955f395f516109a0516020615cb55f395f51808202811583838304141715615b975790509050046109a0525f6109c05260106102e0516020525f5260405f20546109e0525b6032610a605114614559576001610a605101610a60525b600101818118613c8f575b5050610200516102005160016102005103610280510104026102805261022051610220516102a05104026102a0526107208161072061028060045afa5050565b610bc0516145b9576001610be05118156145bb565b5f5b6145da576001610bc051186145d457610be051156145dd565b5f6145dd565b60015b61464657600b610c40527f57726f6e6720696e646578000000000000000000000000000000000000000000610c6052610c4050610c405180610c6001601f825f031636823750506308c379a0610c00526020610c2052601f19601f610c40510116604401610c1cfd5b61072036610c4037610c005161466b5761072081610720610c4060045afa50506147bd565b6020615c55611360396020615c1561138039610bc051614698576020615c15611360396020615c55611380395b6146a36113e0612584565b6113e080516113a05260208101516113c05250610c205161472a57610bc0511561018052610c005161138051808202811583838304141715615b9757905090506101a0526113a0516101c0526113c0516101e052611360516102005261138051610220526147126113e0613bfd565b6113e0610720610c406107208360045afa5050614792565b610bc0511561018052610c005161136051808202811583838304141715615b9757905090506101a0526113a0516101c0526113c0516101e0526113605161020052611380516102205261477e6113e06131ee565b6113e0610720610c406107208360045afa50505b61136051610c405104610c405261138051610c605104610c605261072081610720610c4060045afa50505b565b610bc0516147d4576001610be05118156147d6565b5f5b6147f5576001610bc051186147ef57610be051156147f8565b5f6147f8565b60015b61486157600b610c80527f57726f6e6720696e646578000000000000000000000000000000000000000000610ca052610c8050610c805180610ca001601f825f031636823750506308c379a0610c40526020610c6052601f19601f610c80510116604401610c5cfd5b61486c610cc06125e0565b610cc08051610c80526020810151610ca05250610c0051614896575f81525f602082015250614f4e565b601354610cc0525f610ce0526020615bf5611340396020615c35611360396020615c15611380396020615c556113a0396001610bc051186148f4576113a0516113805261136051611340526020615c156113a0396020615bf5611360395b610720366113c037610c60516149ac577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611ae052611ae051610c0051101561495857610c00516113a051808202811583838304141715615b975790509050611ae0525b610bc0511561018052611ae0516101a052610c80516101c052610ca0516101e05261138051610200526113a05161022052614994611b00613bfd565b611b006107206113c06107208360045afa5050614a14565b610bc0511561018052610c005161138051808202811583838304141715615b9757905090506101a052610c80516101c052610ca0516101e05261138051610200526113a05161022052614a00611ae06131ee565b611ae06107206113c06107208360045afa50505b611380516113c05104611ae0526113a0516113e05104611b0052610c6051614ad257610c2051611ae0511115614a4a575f614a65565b610c0051611b005118614a5e576001614a65565b610c005119155b614b45576008611b20527f536c697070616765000000000000000000000000000000000000000000000000611b4052611b2050611b205180611b4001601f825f031636823750506308c379a0611ae0526020611b0052601f19601f611b20510116604401611afcfd614b45565b610c2051611b00511015614b45576008611b20527f536c697070616765000000000000000000000000000000000000000000000000611b4052611b2050611b205180611b4001601f825f031636823750506308c379a0611ae0526020611b0052601f19601f611b20510116604401611afcfd5b611b0051614b54576001614b5a565b611ae051155b15614b6e575f81525f602082015250614f4e565b61138051611ac05104611ac052610bc051614ba057600a54611ac051808201828110615b975790509050600a55614bb9565b600b54611ac051808201828110615b975790509050600b555b611400516114205180828118828412021890509050611b2052611b2051611b40526114005161142051037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811315614c115780614c1e565b805f038114615b9757805f035b9050611b60525f6032905b80611b8052604036611ba037610bc051614c7257611b805161144051811015615b975760051b6114600151611ba05261142051611b205118614ca857611aa051611bc052614ca8565b611b8051611b60510361144051811015615b975760051b6114600151611bc05261142051611b205118614ca857611aa051611ba0525b611ba051600f611b20516020525f5260405f2055611bc0516010611b20516020525f5260405f2055610cc05115614d42575f611be052611bc05115614d1f576011611b20516020525f5260405f2054611bc051670de0b6b3a7640000810281670de0b6b3a7640000820418615b9757905004611be0525b610ce05160318111615b9757611be0518160051b610d00015260018101610ce052505b611b6051611b805118614d5457614d6a565b6001611b205101611b2052600101818118614c29575b505061142051600755610c40517fb2e76ae99761dc136e598d4a629bb347eccb9532a5f8bbd72e18467c3c34cc98610bc051611b8052611ae051611ba052610be051611bc052611b0051611be0526080611b80a2610cc05115614e5757610cc05163d1c92353611b80526040611b4051611ba05280611bc05280611ba0015f610ce0518083528060051b5f8260328111615b97578015614e2457905b8060051b610d0001518160051b602088010152600101818118614e06575b50508201602001915050905081015050803b15615b97575f611b806106a4611b9c5f855af1614e55573d5f5f3e3d5ffd5b505b611340516323b872dd611b805233611ba05230611bc052611ae051611be0526020611b806064611b9c5f855af1614e90573d5f5f3e3d5ffd5b3d614ea757803b15615b97576001611c0052614ec0565b60203d10615b9757611b80518060011c615b9757611c00525b611c0090505115615b97576113605163a9059cbb611b8052610c4051611ba052611b0051611bc0526020611b806044611b9c5f855af1614f02573d5f5f3e3d5ffd5b3d614f1957803b15615b97576001611be052614f32565b60203d10615b9757611b80518060011c615b9757611be0525b611be090505115615b9757611ae0518152611b00516020820152505b565b61076051604052614f626107e0612f9e565b6107e080516107a05260208101516107c05250610760516040526107a0516060526107c051608052614f95610e40613031565b610e40805160208160051b01806107e0828560045afa505050506107e05115615b97575f60051b6108000151614fce575f81525061588a565b614fd9610e60612584565b610e6051610e4052610e405115615b97576107a05160018103818113615b97579050610e6052600754610e80526107a051606052615018610ec0612703565b610ec051610ea0525f610ec0525f6032905b80610ee052610e605160018101818112615b97579050610e60526107c051610e6051131561505757615854565b604036610f0037610e8051610e605112615080576010610e60516020525f5260405f2054610f20525b610e8051610e6051136150a257600f610e60516020525f5260405f2054610f00525b610ea051610f40526020615c955f395f51610ea0516020615cb55f395f51808202811583838304141715615b97579050905004610ea052610f00516150ee57610f20516150ee57615849565b6011610e60516020525f5260405f2054610f6052610ee0516107e051811015615b975760051b6108000151610f8052610f605161512a57615849565b610f805161513757615849565b610f60516103e88101818110615b97579050610f6052610f4051610e40516fffffffffffffffffffffffffffffffff8111615b97576002810a9050610ea0518015615b975780820490509050610e4051808202811583838304141715615b97579050905004610fa052610f00516151af5760016151b5565b610f2051155b156153a057610f4051610e4051116152b657610ea051610e405110156153a057610f0051610fc052610f005161521057670de0b6b3a7640000610f2051610fa051808202811583838304141715615b97579050905004610fc0525b6107805161525457610ec051610f6051610fc051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f6051610f4051610fc0516020615d155f395f51808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849566153a0565b610f2051610fc052610f20516152fe57610f0051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050610fa0518015615b975780820490509050610fc0525b6107805161536457610ec051610f60516020615d155f395f51610fc051610f4051808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f6051610fc051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610f0051606052610f2051608052610e405160a052610f405160c0526153c7610fe0612acb565b610fe051610fc052670de0b6b3a7640000610f40516020615c955f395f51610fc051808202811583838304141715615b975790509050610e4051808202811583838304141715615b97579050905004610e4051808202811583838304141715615b97579050905004610fe052610e40516020615cb55f395f51610fc051808202811583838304141715615b975790509050610f4051808202811583838304141715615b9757905090500461100052610fe051610f0051808201828110615b97579050905061100051610f2051808201828110615b975790509050808202811583838304141715615b9757905090506110205260403661104037610f4051610e40511161577c57610ea051610e4051106156ab57610e40516020615c955f395f51610fc051808202811583838304141715615b975790509050610ea051610e405103808202811583838304141715615b9757905090500461106052610fe051611020516110005161106051808201828110615b9757905090508015615b975780820490509050610fe0518082811882841102189050905003611040526107805161560457610ec051610f605161104051670de0b6b3a764000061106051610ea051610e4051808202811583838304141715615b9757905090506040526155ad6110806121ea565b61108051808202811583838304141715615b97579050905004808201828110615b975790509050610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f60516110605161104051670de0b6b3a7640000810281670de0b6b3a7640000820418615b97579050610f4051610e4051808202811583838304141715615b97579050905060405261565c6110806121ea565b611080518015615b975780820490509050808201828110615b975790509050610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610fe05161102051611000518015615b975780820490509050610fe0518082811882841102189050905003611040526107805161571e57610ec051610f605161104051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f6051610f4051611040516020615d155f395f51808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b6110005161102051610fe0518015615b975780820490509050611000518082811882841102189050905003611060526107805161581157610ec051610f60516020615d155f395f5161106051610f4051808202811583838304141715615b97579050905004610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec052615849565b610ec051610f605161106051610f8051808202811583838304141715615b97579050905004808201828110615b975790509050610ec0525b60010181811861502a575b505061078051615878576020615c155f395f51610ec0510481525061588a5661588a565b6020615c555f395f51610ec051048152505b565b5f6107a0525f610e005261078051156158de576107a05160318111615b97575f8160051b6107c00152600181016107a05250610e005160318111615b97575f8160051b610e20015260018101610e0052505b610760516040526158f06114a0612f9e565b6114a080516114605260208101516114805250610760516040526114605160605261148051608052615923611b00613031565b611b00805160208160051b01806114a0828560045afa505050506114a05115615b97575f60051b6114c0015115615af8575f6032905b80611b00526011611460516020525f5260405f20546103e88101818110615b97579050611b2052611b00516114a051811015615b975760051b6114c00151611b4052611b2051600f611460516020525f5260405f205460018101818110615b97579050611b4051808202811583838304141715615b97579050905004611b6052611b20516010611460516020525f5260405f205460018101818110615b97579050611b4051808202811583838304141715615b97579050905004611b805261078051615a7c576107a05160318111615b97576020615c155f395f51611b6051048160051b6107c00152600181016107a05250610e005160318111615b97576020615c555f395f51611b8051048160051b610e20015260018101610e005250615acd565b6107a05115615b97575f60051b6107c0018051611b6051808201828110615b975790509050815250610e005115615b97575f60051b610e20018051611b8051808201828110615b9757905090508152505b611480516114605118615adf57615af5565b6001611460510161146052600101818118615959575b50505b6107805115615b5e576020615c155f395f516107a05115615b97575f60051b6107c00151046107a05115615b97575f60051b6107c001526020615c555f395f51610e005115615b97575f60051b610e20015104610e005115615b97575f60051b610e2001525b6107a05160208160051b018083826107a060045afa505050610e005160208160051b016106608301818183610e0060045afa5050505050565b5f80fd216010d9083308c0216021600aa71e8a21600f2d21600efb08a0216021600b492160212c21600b18205f0ebc09cb0c0220c5216021600e98007821600ac70ae7001817c9106a1e250d432160216009eb00381f4c0de80c9c1eea000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd5200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000b8dc3b54c82feb4000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000038400000000000000000000000000000000000000000000000000000000000003490000000000000000000000000000000000000000000000000e1d72befab679d60000000000000000000000000000000000000000000000000078714818f6aa3b0000000000000000000000000000000000000000000000004b97dffd395d09c7000000000000000000000000e0a4c53408f5acf3246c83b9b8bd8d36d5ee38b8

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd520000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000e1d72befab679d60000000000000000000000000000000000000000000000000078714818f6aa3b0000000000000000000000000000000000000000000000000b8dc3b54c82feb400000000000000000000000000000000000000000000000000071afd498d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0a4c53408f5acf3246c83b9b8bd8d36d5ee38b8

-----Decoded View---------------
Arg [0] : _borrowed_token (address): 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E
Arg [1] : _borrowed_precision (uint256): 1
Arg [2] : _collateral_token (address): 0xD533a949740bb3306d119CC777fa900bA034cd52
Arg [3] : _collateral_precision (uint256): 1
Arg [4] : _A (uint256): 30
Arg [5] : _sqrt_band_ratio (uint256): 1017095255431215574
Arg [6] : _log_A_ratio (int256): 33901551675681339
Arg [7] : _base_price (uint256): 832536689573559988
Arg [8] : fee (uint256): 2000000000000000
Arg [9] : admin_fee (uint256): 0
Arg [10] : _price_oracle_contract (address): 0xE0a4C53408f5ACf3246c83b9b8bD8d36D5ee38B8

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [2] : 000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd52
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [4] : 000000000000000000000000000000000000000000000000000000000000001e
Arg [5] : 0000000000000000000000000000000000000000000000000e1d72befab679d6
Arg [6] : 0000000000000000000000000000000000000000000000000078714818f6aa3b
Arg [7] : 0000000000000000000000000000000000000000000000000b8dc3b54c82feb4
Arg [8] : 00000000000000000000000000000000000000000000000000071afd498d0000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 000000000000000000000000e0a4c53408f5acf3246c83b9b8bd8d36d5ee38b8


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.