ETH Price: $3,226.10 (+2.58%)

Contract

0x100dAa78fC509Db39Ef7D04DE0c1ABD299f4C6CE
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Add_collateral211795642024-11-13 15:07:3514 hrs ago1731510455IN
0x100dAa78...299f4C6CE
0 ETH0.0829992598.20038462
Add_collateral211795262024-11-13 14:59:5914 hrs ago1731509999IN
0x100dAa78...299f4C6CE
0 ETH0.09113584107.83214126
Borrow_more211795052024-11-13 14:55:4714 hrs ago1731509747IN
0x100dAa78...299f4C6CE
0 ETH0.09865484107.41438063
Borrow_more211794892024-11-13 14:52:3514 hrs ago1731509555IN
0x100dAa78...299f4C6CE
0 ETH0.09370151100.7747979
Borrow_more211723702024-11-12 15:02:3538 hrs ago1731423755IN
0x100dAa78...299f4C6CE
0 ETH0.0337996626.65576324
Borrow_more211709512024-11-12 10:17:3543 hrs ago1731406655IN
0x100dAa78...299f4C6CE
0 ETH0.0383004546.83799303
Repay_extended211573702024-11-10 12:48:473 days ago1731242927IN
0x100dAa78...299f4C6CE
0 ETH0.0257721816.02992895
Add_collateral211570892024-11-10 11:52:233 days ago1731239543IN
0x100dAa78...299f4C6CE
0 ETH0.0125973816.91010642
Borrow_more211570762024-11-10 11:49:473 days ago1731239387IN
0x100dAa78...299f4C6CE
0 ETH0.012659616.4
Borrow_more211557042024-11-10 7:14:353 days ago1731222875IN
0x100dAa78...299f4C6CE
0 ETH0.0088199411.34307065
Add_collateral211487372024-11-09 7:55:114 days ago1731138911IN
0x100dAa78...299f4C6CE
0 ETH0.0093125910.38868464
Borrow_more211487122024-11-09 7:50:114 days ago1731138611IN
0x100dAa78...299f4C6CE
0 ETH0.0104601511.24458844
Borrow_more211461712024-11-08 23:20:355 days ago1731108035IN
0x100dAa78...299f4C6CE
0 ETH0.008389419.07562915
Add_collateral211422742024-11-08 10:18:115 days ago1731061091IN
0x100dAa78...299f4C6CE
0 ETH0.0111727714.0843282
Add_collateral211422642024-11-08 10:16:115 days ago1731060971IN
0x100dAa78...299f4C6CE
0 ETH0.0115427414.04223233
Borrow_more211422412024-11-08 10:11:355 days ago1731060695IN
0x100dAa78...299f4C6CE
0 ETH0.0135188515.88355142
Borrow_more211422262024-11-08 10:08:355 days ago1731060515IN
0x100dAa78...299f4C6CE
0 ETH0.0127343313.39155136
Add_collateral211421572024-11-08 9:54:475 days ago1731059687IN
0x100dAa78...299f4C6CE
0 ETH0.008051639.96666121
Add_collateral211421382024-11-08 9:50:475 days ago1731059447IN
0x100dAa78...299f4C6CE
0 ETH0.0088916810.25284223
Borrow_more211421202024-11-08 9:47:115 days ago1731059231IN
0x100dAa78...299f4C6CE
0 ETH0.0092697910.1019701
Borrow_more211421122024-11-08 9:45:355 days ago1731059135IN
0x100dAa78...299f4C6CE
0 ETH0.0116420710.86617191
Add_collateral211329052024-11-07 2:51:357 days ago1730947895IN
0x100dAa78...299f4C6CE
0 ETH0.0178926321.8
Add_collateral211327642024-11-07 2:23:237 days ago1730946203IN
0x100dAa78...299f4C6CE
0 ETH0.0160054818.66111577
Borrow_more211326992024-11-07 2:10:237 days ago1730945423IN
0x100dAa78...299f4C6CE
0 ETH0.0174507318.4617418
Borrow_more211326822024-11-07 2:06:597 days ago1730945219IN
0x100dAa78...299f4C6CE
0 ETH0.016997219.96903508
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
174322252023-06-08 1:05:11525 days ago1686186311  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
crvUSD Controller

Compiler Version
vyper:0.3.7

Optimization Enabled:
N/A

Other Settings:
None license

Contract Source Code (Vyper language format)

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

interface LLAMMA:
    def A() -> uint256: view
    def get_p() -> uint256: view
    def get_base_price() -> uint256: view
    def active_band() -> int256: view
    def active_band_with_skip() -> int256: view
    def p_oracle_up(n: int256) -> uint256: view
    def p_oracle_down(n: int256) -> uint256: view
    def deposit_range(user: address, amount: uint256, n1: int256, n2: int256): nonpayable
    def read_user_tick_numbers(_for: address) -> int256[2]: view
    def get_sum_xy(user: address) -> uint256[2]: view
    def withdraw(user: address, frac: uint256) -> uint256[2]: nonpayable
    def get_x_down(user: address) -> uint256: view
    def get_rate_mul() -> uint256: view
    def set_rate(rate: uint256) -> uint256: nonpayable
    def set_fee(fee: uint256): nonpayable
    def set_admin_fee(fee: uint256): nonpayable
    def price_oracle() -> uint256: view
    def can_skip_bands(n_end: int256) -> bool: view
    def set_price_oracle(price_oracle: PriceOracle): nonpayable
    def admin_fees_x() -> uint256: view
    def admin_fees_y() -> uint256: view
    def reset_admin_fees(): nonpayable
    def has_liquidity(user: address) -> bool: view
    def bands_x(n: int256) -> uint256: view
    def bands_y(n: int256) -> uint256: view
    def set_callback(user: address): nonpayable

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

interface WETH:
    def deposit(): payable
    def withdraw(_amount: uint256): nonpayable

interface MonetaryPolicy:
    def rate_write() -> uint256: nonpayable

interface Factory:
    def stablecoin() -> address: view
    def admin() -> address: view
    def fee_receiver() -> address: view
    def WETH() -> address: view

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


event UserState:
    user: indexed(address)
    collateral: uint256
    debt: uint256
    n1: int256
    n2: int256
    liquidation_discount: uint256

event Borrow:
    user: indexed(address)
    collateral_increase: uint256
    loan_increase: uint256

event Repay:
    user: indexed(address)
    collateral_decrease: uint256
    loan_decrease: uint256

event RemoveCollateral:
    user: indexed(address)
    collateral_decrease: uint256

event Liquidate:
    liquidator: indexed(address)
    user: indexed(address)
    collateral_received: uint256
    stablecoin_received: uint256
    debt: uint256

event SetMonetaryPolicy:
    monetary_policy: address

event SetBorrowingDiscounts:
    loan_discount: uint256
    liquidation_discount: uint256

event CollectFees:
    amount: uint256
    new_supply: uint256


struct Loan:
    initial_debt: uint256
    rate_mul: uint256

struct Position:
    user: address
    x: uint256
    y: uint256
    debt: uint256
    health: int256

struct CallbackData:
    active_band: int256
    stablecoins: uint256
    collateral: uint256


FACTORY: immutable(Factory)
STABLECOIN: immutable(ERC20)
MAX_LOAN_DISCOUNT: constant(uint256) = 5 * 10**17
MIN_LIQUIDATION_DISCOUNT: constant(uint256) = 10**16 # Start liquidating when threshold reached
MAX_TICKS: constant(int256) = 50
MAX_TICKS_UINT: constant(uint256) = 50
MIN_TICKS: constant(int256) = 4
MAX_SKIP_TICKS: constant(uint256) = 1024
MAX_P_BASE_BANDS: constant(int256) = 5

MAX_RATE: constant(uint256) = 43959106799  # 400% APY

loan: HashMap[address, Loan]
liquidation_discounts: public(HashMap[address, uint256])
_total_debt: Loan

loans: public(address[2**64 - 1])  # Enumerate existing loans
loan_ix: public(HashMap[address, uint256])  # Position of the loan in the list
n_loans: public(uint256)  # Number of nonzero loans

minted: public(uint256)
redeemed: public(uint256)

monetary_policy: public(MonetaryPolicy)
liquidation_discount: public(uint256)
loan_discount: public(uint256)

COLLATERAL_TOKEN: immutable(ERC20)
COLLATERAL_PRECISION: immutable(uint256)

AMM: immutable(LLAMMA)
A: immutable(uint256)
Aminus1: immutable(uint256)
LOG2_A_RATIO: immutable(int256)  # log(A / (A - 1))
SQRT_BAND_RATIO: immutable(uint256)

MAX_ADMIN_FEE: constant(uint256) = 10**18  # 100%
MIN_FEE: constant(uint256) = 10**6  # 1e-12, still needs to be above 0
MAX_FEE: constant(uint256) = 10**17  # 10%

USE_ETH: immutable(bool)

CALLBACK_DEPOSIT: constant(bytes4) = method_id("callback_deposit(address,uint256,uint256,uint256,uint256[])", output_type=bytes4)
CALLBACK_REPAY: constant(bytes4) = method_id("callback_repay(address,uint256,uint256,uint256,uint256[])", output_type=bytes4)
CALLBACK_LIQUIDATE: constant(bytes4) = method_id("callback_liquidate(address,uint256,uint256,uint256,uint256[])", output_type=bytes4)

DEAD_SHARES: constant(uint256) = 1000

MAX_ETH_GAS: constant(uint256) = 10000  # Forward this much gas to ETH transfers (2300 is what send() does)


@external
def __init__(
        collateral_token: address,
        monetary_policy: address,
        loan_discount: uint256,
        liquidation_discount: uint256,
        amm: address):
    """
    @notice Controller constructor deployed by the factory from blueprint
    @param collateral_token Token to use for collateral
    @param monetary_policy Address of monetary policy
    @param loan_discount Discount of the maximum loan size compare to get_x_down() value
    @param liquidation_discount Discount of the maximum loan size compare to
           get_x_down() for "bad liquidation" purposes
    @param amm AMM address (Already deployed from blueprint)
    """
    FACTORY = Factory(msg.sender)
    stablecoin: ERC20 = ERC20(Factory(msg.sender).stablecoin())
    STABLECOIN = stablecoin
    assert stablecoin.decimals() == 18

    self.monetary_policy = MonetaryPolicy(monetary_policy)

    self.liquidation_discount = liquidation_discount
    self.loan_discount = loan_discount
    self._total_debt.rate_mul = 10**18

    AMM = LLAMMA(amm)
    _A: uint256 = LLAMMA(amm).A()
    A = _A
    Aminus1 = _A - 1
    LOG2_A_RATIO = self.log2(_A * 10**18 / unsafe_sub(_A, 1))

    COLLATERAL_TOKEN = ERC20(collateral_token)
    COLLATERAL_PRECISION = pow_mod256(10, 18 - ERC20(collateral_token).decimals())

    SQRT_BAND_RATIO = isqrt(unsafe_div(10**36 * _A, unsafe_sub(_A, 1)))

    stablecoin.approve(msg.sender, max_value(uint256))

    if Factory(msg.sender).WETH() == collateral_token:
        USE_ETH = True


@payable
@external
def __default__():
    if msg.value > 0:
        assert USE_ETH
    assert len(msg.data) == 0


@internal
@pure
def log2(_x: uint256) -> int256:
    """
    @notice int(1e18 * log2(_x / 1e18))
    """
    # adapted from: https://medium.com/coinmonks/9aef8515136e
    # and vyper log implementation
    # Might use more optimal solmate's log
    inverse: bool = _x < 10**18
    res: uint256 = 0
    x: uint256 = _x
    if inverse:
        x = 10**36 / x
    t: uint256 = 2**7
    for i in range(8):
        p: uint256 = pow_mod256(2, t)
        if x >= unsafe_mul(p, 10**18):
            x = unsafe_div(x, p)
            res = unsafe_add(unsafe_mul(t, 10**18), res)
        t = unsafe_div(t, 2)
    d: uint256 = 10**18
    for i in range(34):  # 10 decimals: math.log(10**10, 2) == 33.2. Need more?
        if (x >= 2 * 10**18):
            res = unsafe_add(res, d)
            x = unsafe_div(x, 2)
        x = unsafe_div(unsafe_mul(x, x), 10**18)
        d = unsafe_div(d, 2)
    if inverse:
        return -convert(res, int256)
    else:
        return convert(res, int256)


@external
@view
def factory() -> Factory:
    """
    @notice Address of the factory
    """
    return FACTORY


@external
@view
def amm() -> LLAMMA:
    """
    @notice Address of the AMM
    """
    return AMM


@external
@view
def collateral_token() -> ERC20:
    """
    @notice Address of the collateral token
    """
    return COLLATERAL_TOKEN


@internal
def _rate_mul_w() -> uint256:
    """
    @notice Getter for rate_mul (the one which is 1.0+) from the AMM
    """
    rate: uint256 = min(self.monetary_policy.rate_write(), MAX_RATE)
    return AMM.set_rate(rate)


@internal
def _debt(user: address) -> (uint256, uint256):
    """
    @notice Get the value of debt and rate_mul and update the rate_mul counter
    @param user User address
    @return (debt, rate_mul)
    """
    rate_mul: uint256 = self._rate_mul_w()
    loan: Loan = self.loan[user]
    if loan.initial_debt == 0:
        return (0, rate_mul)
    else:
        return (loan.initial_debt * rate_mul / loan.rate_mul, rate_mul)


@internal
@view
def _debt_ro(user: address) -> uint256:
    """
    @notice Get the value of debt without changing the state
    @param user User address
    @return Value of debt
    """
    rate_mul: uint256 = AMM.get_rate_mul()
    loan: Loan = self.loan[user]
    if loan.initial_debt == 0:
        return 0
    else:
        return loan.initial_debt * rate_mul / loan.rate_mul


@external
@view
@nonreentrant('lock')
def debt(user: address) -> uint256:
    """
    @notice Get the value of debt without changing the state
    @param user User address
    @return Value of debt
    """
    return self._debt_ro(user)


@external
@view
@nonreentrant('lock')
def loan_exists(user: address) -> bool:
    """
    @notice Check whether there is a loan of `user` in existence
    """
    return self.loan[user].initial_debt > 0


# No decorator because used in monetary policy
@external
@view
def total_debt() -> uint256:
    """
    @notice Total debt of this controller
    """
    rate_mul: uint256 = AMM.get_rate_mul()
    loan: Loan = self._total_debt
    return loan.initial_debt * rate_mul / loan.rate_mul


@internal
@view
def get_y_effective(collateral: uint256, N: uint256, discount: uint256) -> uint256:
    """
    @notice Intermediary method which calculates y_effective defined as x_effective / p_base,
            however discounted by loan_discount.
            x_effective is an amount which can be obtained from collateral when liquidating
    @param collateral Amount of collateral to get the value for
    @param N Number of bands the deposit is made into
    @param discount Loan discount at 1e18 base (e.g. 1e18 == 100%)
    @return y_effective
    """
    # x_effective = sum_{i=0..N-1}(y / N * p(n_{n1+i})) =
    # = y / N * p_oracle_up(n1) * sqrt((A - 1) / A) * sum_{0..N-1}(((A-1) / A)**k)
    # === d_y_effective * p_oracle_up(n1) * sum(...) === y_effective * p_oracle_up(n1)
    # d_y_effective = y / N / sqrt(A / (A - 1))
    # d_y_effective: uint256 = collateral * unsafe_sub(10**18, discount) / (SQRT_BAND_RATIO * N)
    # Make some extra discount to always deposit lower when we have DEAD_SHARES rounding
    d_y_effective: uint256 = collateral * unsafe_sub(
        10**18, min(discount + (DEAD_SHARES * 10**18) / max(collateral / N, DEAD_SHARES), 10**18)
    ) / (SQRT_BAND_RATIO * N)
    y_effective: uint256 = d_y_effective
    for i in range(1, MAX_TICKS_UINT):
        if i == N:
            break
        d_y_effective = unsafe_div(d_y_effective * Aminus1, A)
        y_effective = unsafe_add(y_effective, d_y_effective)
    return y_effective


@internal
@view
def _calculate_debt_n1(collateral: uint256, debt: uint256, N: uint256) -> int256:
    """
    @notice Calculate the upper band number for the deposit to sit in to support
            the given debt. Reverts if requested debt is too high.
    @param collateral Amount of collateral (at its native precision)
    @param debt Amount of requested debt
    @param N Number of bands to deposit into
    @return Upper band n1 (n1 <= n2) to deposit into. Signed integer
    """
    assert debt > 0, "No loan"
    n0: int256 = AMM.active_band()
    p_base: uint256 = AMM.p_oracle_up(n0)

    # x_effective = y / N * p_oracle_up(n1) * sqrt((A - 1) / A) * sum_{0..N-1}(((A-1) / A)**k)
    # === d_y_effective * p_oracle_up(n1) * sum(...) === y_effective * p_oracle_up(n1)
    # d_y_effective = y / N / sqrt(A / (A - 1))
    y_effective: uint256 = self.get_y_effective(collateral * COLLATERAL_PRECISION, N, self.loan_discount)
    # p_oracle_up(n1) = base_price * ((A - 1) / A)**n1

    # We borrow up until min band touches p_oracle,
    # or it touches non-empty bands which cannot be skipped.
    # We calculate required n1 for given (collateral, debt),
    # and if n1 corresponds to price_oracle being too high, or unreachable band
    # - we revert.

    # n1 is band number based on adiabatic trading, e.g. when p_oracle ~ p
    y_effective = y_effective * p_base / (debt + 1)  # Now it's a ratio

    # n1 = floor(log2(y_effective) / self.logAratio)
    # EVM semantics is not doing floor unlike Python, so we do this
    assert y_effective > 0, "Amount too low"
    n1: int256 = self.log2(y_effective)  # <- switch to faster ln() XXX?
    if n1 < 0:
        n1 -= LOG2_A_RATIO - 1  # This is to deal with vyper's rounding of negative numbers
    n1 /= LOG2_A_RATIO

    n1 = min(n1, 1024 - convert(N, int256)) + n0
    if n1 <= n0:
        assert AMM.can_skip_bands(n1 - 1), "Debt too high"

    # Let's not rely on active_band corresponding to price_oracle:
    # this will be not correct if we are in the area of empty bands
    assert AMM.p_oracle_up(n1) < AMM.price_oracle(), "Debt too high"

    return n1


@internal
@view
def max_p_base() -> uint256:
    """
    @notice Calculate max base price including skipping bands
    """
    p_oracle: uint256 = AMM.price_oracle()
    # Should be correct unless price changes suddenly by MAX_P_BASE_BANDS+ bands
    n1: int256 = unsafe_div(self.log2(AMM.get_base_price() * 10**18 / p_oracle), LOG2_A_RATIO) + MAX_P_BASE_BANDS
    p_base: uint256 = AMM.p_oracle_up(n1)
    n_min: int256 = AMM.active_band_with_skip()

    for i in range(MAX_SKIP_TICKS + 1):
        n1 -= 1
        if n1 <= n_min:
            break
        p_base_prev: uint256 = p_base
        p_base = unsafe_div(p_base * A, Aminus1)
        if p_base > p_oracle:
            return p_base_prev

    return p_base


@external
@view
@nonreentrant('lock')
def max_borrowable(collateral: uint256, N: uint256) -> uint256:
    """
    @notice Calculation of maximum which can be borrowed (details in comments)
    @param collateral Collateral amount against which to borrow
    @param N number of bands to have the deposit into
    @return Maximum amount of stablecoin to borrow
    """
    # Calculation of maximum which can be borrowed.
    # It corresponds to a minimum between the amount corresponding to price_oracle
    # and the one given by the min reachable band.
    #
    # Given by p_oracle (perhaps needs to be multiplied by (A - 1) / A to account for mid-band effects)
    # x_max ~= y_effective * p_oracle
    #
    # Given by band number:
    # if n1 is the lowest empty band in the AMM
    # xmax ~= y_effective * amm.p_oracle_up(n1)
    #
    # When n1 -= 1:
    # p_oracle_up *= A / (A - 1)

    y_effective: uint256 = self.get_y_effective(collateral * COLLATERAL_PRECISION, N, self.loan_discount)

    x: uint256 = unsafe_sub(max(unsafe_div(y_effective * self.max_p_base(), 10**18), 1), 1)
    x = unsafe_div(x * (10**18 - 10**14), 10**18)  # Make it a bit smaller
    return min(x, STABLECOIN.balanceOf(self))  # Cannot borrow beyond the amount of coins Controller has


@external
@view
@nonreentrant('lock')
def min_collateral(debt: uint256, N: uint256) -> uint256:
    """
    @notice Minimal amount of collateral required to support debt
    @param debt The debt to support
    @param N Number of bands to deposit into
    @return Minimal collateral required
    """
    # Add N**2 to account for precision loss in multiple bands, e.g. N * 1 / (y/N) = N**2 / y
    return unsafe_div(unsafe_div(debt * 10**18 / self.max_p_base() * 10**18 / self.get_y_effective(10**18, N, self.loan_discount) + N * (N + 2 * DEAD_SHARES), COLLATERAL_PRECISION) * 10**18, 10**18 - 10**14)


@external
@view
@nonreentrant('lock')
def calculate_debt_n1(collateral: uint256, debt: uint256, N: uint256) -> int256:
    """
    @notice Calculate the upper band number for the deposit to sit in to support
            the given debt. Reverts if requested debt is too high.
    @param collateral Amount of collateral (at its native precision)
    @param debt Amount of requested debt
    @param N Number of bands to deposit into
    @return Upper band n1 (n1 <= n2) to deposit into. Signed integer
    """
    return self._calculate_debt_n1(collateral, debt, N)


@internal
def _deposit_collateral(amount: uint256, mvalue: uint256):
    """
    Deposits raw ETH, WETH or both at the same time
    """
    if not USE_ETH:
        assert mvalue == 0  # dev: Not accepting ETH
    diff: uint256 = amount - mvalue  # dev: Incorrect ETH amount
    if mvalue > 0:
        WETH(COLLATERAL_TOKEN.address).deposit(value=mvalue)
        assert COLLATERAL_TOKEN.transfer(AMM.address, mvalue)
    if diff > 0:
        assert COLLATERAL_TOKEN.transferFrom(msg.sender, AMM.address, diff, default_return_value=True)


@internal
def _withdraw_collateral(_for: address, amount: uint256, use_eth: bool):
    if use_eth and USE_ETH:
        assert COLLATERAL_TOKEN.transferFrom(AMM.address, self, amount)
        WETH(COLLATERAL_TOKEN.address).withdraw(amount)
        raw_call(_for, b"", value=amount, gas=MAX_ETH_GAS)
    else:
        assert COLLATERAL_TOKEN.transferFrom(AMM.address, _for, amount, default_return_value=True)


@internal
def execute_callback(callbacker: address, callback_sig: bytes4,
                     user: address, stablecoins: uint256, collateral: uint256, debt: uint256,
                     callback_args: DynArray[uint256, 5]) -> CallbackData:
    assert callbacker != COLLATERAL_TOKEN.address

    data: CallbackData = empty(CallbackData)
    data.active_band = AMM.active_band()
    band_x: uint256 = AMM.bands_x(data.active_band)
    band_y: uint256 = AMM.bands_y(data.active_band)

    # Callback
    response: Bytes[64] = raw_call(
        callbacker,
        concat(callback_sig, _abi_encode(user, stablecoins, collateral, debt, callback_args)),
        max_outsize=64
    )
    data.stablecoins = convert(slice(response, 0, 32), uint256)
    data.collateral = convert(slice(response, 32, 32), uint256)

    # Checks after callback
    assert data.active_band == AMM.active_band()
    assert band_x == AMM.bands_x(data.active_band)
    assert band_y == AMM.bands_y(data.active_band)

    return data

@internal
def _create_loan(mvalue: uint256, collateral: uint256, debt: uint256, N: uint256, transfer_coins: bool):
    assert self.loan[msg.sender].initial_debt == 0, "Loan already created"
    assert N > MIN_TICKS-1, "Need more ticks"
    assert N < MAX_TICKS+1, "Need less ticks"

    n1: int256 = self._calculate_debt_n1(collateral, debt, N)
    n2: int256 = n1 + convert(N - 1, int256)

    rate_mul: uint256 = self._rate_mul_w()
    self.loan[msg.sender] = Loan({initial_debt: debt, rate_mul: rate_mul})
    liquidation_discount: uint256 = self.liquidation_discount
    self.liquidation_discounts[msg.sender] = liquidation_discount

    n_loans: uint256 = self.n_loans
    self.loans[n_loans] = msg.sender
    self.loan_ix[msg.sender] = n_loans
    self.n_loans = unsafe_add(n_loans, 1)

    total_debt: uint256 = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul + debt
    self._total_debt.initial_debt = total_debt
    self._total_debt.rate_mul = rate_mul

    AMM.deposit_range(msg.sender, collateral, n1, n2)
    self.minted += debt

    if transfer_coins:
        self._deposit_collateral(collateral, mvalue)
        STABLECOIN.transfer(msg.sender, debt)

    log UserState(msg.sender, collateral, debt, n1, n2, liquidation_discount)
    log Borrow(msg.sender, collateral, debt)


@payable
@external
@nonreentrant('lock')
def create_loan(collateral: uint256, debt: uint256, N: uint256):
    """
    @notice Create loan
    @param collateral Amount of collateral to use
    @param debt Stablecoin debt to take
    @param N Number of bands to deposit into (to do autoliquidation-deliquidation),
           can be from MIN_TICKS to MAX_TICKS
    """
    self._create_loan(msg.value, collateral, debt, N, True)


@payable
@external
@nonreentrant('lock')
def create_loan_extended(collateral: uint256, debt: uint256, N: uint256, callbacker: address, callback_args: DynArray[uint256,5]):
    """
    @notice Create loan but pass stablecoin to a callback first so that it can build leverage
    @param collateral Amount of collateral to use
    @param debt Stablecoin debt to take
    @param N Number of bands to deposit into (to do autoliquidation-deliquidation),
           can be from MIN_TICKS to MAX_TICKS
    @param callbacker Address of the callback contract
    @param callback_args Extra arguments for the callback (up to 5) such as min_amount etc
    """
    # Before callback
    STABLECOIN.transfer(callbacker, debt)

    # Callback
    # If there is any unused debt, callbacker can send it to the user
    more_collateral: uint256 = self.execute_callback(
        callbacker, CALLBACK_DEPOSIT, msg.sender, 0, collateral, debt, callback_args).collateral

    # After callback
    self._deposit_collateral(collateral, msg.value)
    assert COLLATERAL_TOKEN.transferFrom(callbacker, AMM.address, more_collateral, default_return_value=True)
    self._create_loan(0, collateral + more_collateral, debt, N, False)


@internal
def _add_collateral_borrow(d_collateral: uint256, d_debt: uint256, _for: address, remove_collateral: bool):
    """
    @notice Internal method to borrow and add or remove collateral
    @param d_collateral Amount of collateral to add
    @param d_debt Amount of debt increase
    @param _for Address to transfer tokens to
    @param remove_collateral Remove collateral instead of adding
    """
    debt: uint256 = 0
    rate_mul: uint256 = 0
    debt, rate_mul = self._debt(_for)
    assert debt > 0, "Loan doesn't exist"
    debt += d_debt
    ns: int256[2] = AMM.read_user_tick_numbers(_for)
    size: uint256 = convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256)

    xy: uint256[2] = AMM.withdraw(_for, 10**18)
    assert xy[0] == 0, "Already in underwater mode"
    if remove_collateral:
        xy[1] -= d_collateral
    else:
        xy[1] += d_collateral
    n1: int256 = self._calculate_debt_n1(xy[1], debt, size)
    n2: int256 = n1 + unsafe_sub(ns[1], ns[0])

    AMM.deposit_range(_for, xy[1], n1, n2)
    self.loan[_for] = Loan({initial_debt: debt, rate_mul: rate_mul})
    liquidation_discount: uint256 = self.liquidation_discount
    self.liquidation_discounts[_for] = liquidation_discount

    if d_debt != 0:
        total_debt: uint256 = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul + d_debt
        self._total_debt.initial_debt = total_debt
        self._total_debt.rate_mul = rate_mul

    if remove_collateral:
        log RemoveCollateral(_for, d_collateral)
    else:
        log Borrow(_for, d_collateral, d_debt)
    log UserState(_for, xy[1], debt, n1, n2, liquidation_discount)


@payable
@external
@nonreentrant('lock')
def add_collateral(collateral: uint256, _for: address = msg.sender):
    """
    @notice Add extra collateral to avoid bad liqidations
    @param collateral Amount of collateral to add
    @param _for Address to add collateral for
    """
    if collateral == 0:
        return
    self._add_collateral_borrow(collateral, 0, _for, False)
    self._deposit_collateral(collateral, msg.value)


@external
@nonreentrant('lock')
def remove_collateral(collateral: uint256, use_eth: bool = True):
    """
    @notice Remove some collateral without repaying the debt
    @param collateral Amount of collateral to remove
    @param use_eth Use wrapping/unwrapping if collateral is ETH
    """
    if collateral == 0:
        return
    self._add_collateral_borrow(collateral, 0, msg.sender, True)
    self._withdraw_collateral(msg.sender, collateral, use_eth)


@payable
@external
@nonreentrant('lock')
def borrow_more(collateral: uint256, debt: uint256):
    """
    @notice Borrow more stablecoins while adding more collateral (not necessary)
    @param collateral Amount of collateral to add
    @param debt Amount of stablecoin debt to take
    """
    if debt == 0:
        return
    self._add_collateral_borrow(collateral, debt, msg.sender, False)
    if collateral != 0:
        self._deposit_collateral(collateral, msg.value)
    STABLECOIN.transfer(msg.sender, debt)
    self.minted += debt


@internal
def _remove_from_list(_for: address):
    last_loan_ix: uint256 = self.n_loans - 1
    loan_ix: uint256 = self.loan_ix[_for]
    assert self.loans[loan_ix] == _for  # dev: should never fail but safety first
    self.loan_ix[_for] = 0
    if loan_ix < last_loan_ix:  # Need to replace
        last_loan: address = self.loans[last_loan_ix]
        self.loans[loan_ix] = last_loan
        self.loan_ix[last_loan] = loan_ix
    self.n_loans = last_loan_ix


@payable
@external
@nonreentrant('lock')
def repay(_d_debt: uint256, _for: address = msg.sender, max_active_band: int256 = 2**255-1, use_eth: bool = True):
    """
    @notice Repay debt (partially or fully)
    @param _d_debt The amount of debt to repay. If higher than the current debt - will do full repayment
    @param _for The user to repay the debt for
    @param max_active_band Don't allow active band to be higher than this (to prevent front-running the repay)
    @param use_eth Use wrapping/unwrapping if collateral is ETH
    """
    if _d_debt == 0:
        return
    # Or repay all for MAX_UINT256
    # Withdraw if debt become 0
    debt: uint256 = 0
    rate_mul: uint256 = 0
    debt, rate_mul = self._debt(_for)
    assert debt > 0, "Loan doesn't exist"
    d_debt: uint256 = min(debt, _d_debt)
    debt = unsafe_sub(debt, d_debt)

    if debt == 0:
        # Allow to withdraw all assets even when underwater
        xy: uint256[2] = AMM.withdraw(_for, 10**18)
        if xy[0] > 0:
            # Only allow full repayment when underwater for the sender to do
            assert _for == msg.sender
            STABLECOIN.transferFrom(AMM.address, _for, xy[0])
        if xy[1] > 0:
            self._withdraw_collateral(_for, xy[1], use_eth)
        log UserState(_for, 0, 0, 0, 0, 0)
        log Repay(_for, xy[1], d_debt)
        self._remove_from_list(_for)

    else:
        active_band: int256 = AMM.active_band_with_skip()
        assert active_band <= max_active_band

        ns: int256[2] = AMM.read_user_tick_numbers(_for)
        size: uint256 = convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256)
        liquidation_discount: uint256 = 0

        if ns[0] > active_band:
            # Not in liquidation - can move bands
            xy: uint256[2] = AMM.withdraw(_for, 10**18)
            n1: int256 = self._calculate_debt_n1(xy[1], debt, size)
            n2: int256 = n1 + unsafe_sub(ns[1], ns[0])
            AMM.deposit_range(_for, xy[1], n1, n2)
            liquidation_discount = self.liquidation_discount
            self.liquidation_discounts[_for] = liquidation_discount
            log UserState(_for, xy[1], debt, n1, n2, liquidation_discount)
            log Repay(_for, 0, d_debt)
        else:
            # Underwater - cannot move band but can avoid a bad liquidation
            liquidation_discount = self.liquidation_discounts[_for]
            log UserState(_for, max_value(uint256), debt, ns[0], ns[1], liquidation_discount)
            log Repay(_for, 0, d_debt)

        if _for != msg.sender:
            # Doesn't allow non-sender to repay in a way which ends with unhealthy state
            # full = False to make this condition non-manipulatable (and also cheaper on gas)
            assert self._health(_for, debt, False, liquidation_discount) > 0

    # If we withdrew already - will burn less!
    STABLECOIN.transferFrom(msg.sender, self, d_debt)  # fail: insufficient funds
    self.redeemed += d_debt

    self.loan[_for] = Loan({initial_debt: debt, rate_mul: rate_mul})
    total_debt: uint256 = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul
    self._total_debt.initial_debt = unsafe_sub(max(total_debt, d_debt), d_debt)
    self._total_debt.rate_mul = rate_mul


@external
@nonreentrant('lock')
def repay_extended(callbacker: address, callback_args: DynArray[uint256,5]):
    """
    @notice Repay loan but get a stablecoin for that from callback (to deleverage)
    @param callbacker Address of the callback contract
    @param callback_args Extra arguments for the callback (up to 5) such as min_amount etc
    """
    # Before callback
    ns: int256[2] = AMM.read_user_tick_numbers(msg.sender)
    xy: uint256[2] = AMM.withdraw(msg.sender, 10**18)
    debt: uint256 = 0
    rate_mul: uint256 = 0
    debt, rate_mul = self._debt(msg.sender)
    COLLATERAL_TOKEN.transferFrom(AMM.address, callbacker, xy[1], default_return_value=True)

    cb: CallbackData = self.execute_callback(
        callbacker, CALLBACK_REPAY, msg.sender, xy[0], xy[1], debt, callback_args)

    # After callback
    total_stablecoins: uint256 = cb.stablecoins + xy[0]
    assert total_stablecoins > 0  # dev: no coins to repay

    # d_debt: uint256 = min(debt, total_stablecoins)

    d_debt: uint256 = 0

    # If we have more stablecoins than the debt - full repayment and closing the position
    if total_stablecoins >= debt:
        d_debt = debt
        debt = 0
        self._remove_from_list(msg.sender)

        # Transfer debt to self, everything else to sender
        if cb.stablecoins > 0:
            STABLECOIN.transferFrom(callbacker, self, cb.stablecoins)
        if xy[0] > 0:
            STABLECOIN.transferFrom(AMM.address, self, xy[0])
        if total_stablecoins > d_debt:
            STABLECOIN.transfer(msg.sender, unsafe_sub(total_stablecoins, d_debt))
        if cb.collateral > 0:
            assert COLLATERAL_TOKEN.transferFrom(callbacker, msg.sender, cb.collateral, default_return_value=True)

        log UserState(msg.sender, 0, 0, 0, 0, 0)

    # Else - partial repayment -> deleverage, but only if we are not underwater
    else:
        size: uint256 = convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256)
        assert ns[0] > cb.active_band
        d_debt = cb.stablecoins  # cb.stablecoins <= total_stablecoins < debt
        debt = unsafe_sub(debt, cb.stablecoins)

        # Not in liquidation - can move bands
        n1: int256 = self._calculate_debt_n1(cb.collateral, debt, size)
        n2: int256 = n1 + unsafe_sub(ns[1], ns[0])
        AMM.deposit_range(msg.sender, cb.collateral, n1, n2)
        liquidation_discount: uint256 = self.liquidation_discount
        self.liquidation_discounts[msg.sender] = liquidation_discount

        assert COLLATERAL_TOKEN.transferFrom(callbacker, AMM.address, cb.collateral, default_return_value=True)
        # Stablecoin is all spent to repay debt -> all goes to self
        STABLECOIN.transferFrom(callbacker, self, cb.stablecoins)
        # We are above active band, so xy[0] is 0 anyway

        log UserState(msg.sender, cb.collateral, debt, n1, n2, liquidation_discount)
        xy[1] = 0

        # No need to check _health() because it's the sender

    # Common calls which we will do regardless of whether it's a full repay or not
    log Repay(msg.sender, xy[1], d_debt)
    self.redeemed += d_debt
    self.loan[msg.sender] = Loan({initial_debt: debt, rate_mul: rate_mul})
    total_debt: uint256 = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul
    self._total_debt.initial_debt = unsafe_sub(max(total_debt, d_debt), d_debt)
    self._total_debt.rate_mul = rate_mul


@internal
@view
def _health(user: address, debt: uint256, full: bool, liquidation_discount: uint256) -> int256:
    """
    @notice Returns position health normalized to 1e18 for the user.
            Liquidation starts when < 0, however devaluation of collateral doesn't cause liquidation
    @param user User address to calculate health for
    @param debt The amount of debt to calculate health for
    @param full Whether to take into account the price difference above the highest user's band
    @param liquidation_discount Liquidation discount to use (can be 0)
    @return Health: > 0 = good.
    """
    assert debt > 0, "Loan doesn't exist"
    health: int256 = 10**18
    if liquidation_discount > 0:
        health -= convert(liquidation_discount, int256)
    health = unsafe_div(convert(AMM.get_x_down(user), int256) * health, convert(debt, int256)) - 10**18

    if full:
        ns: int256[2] = AMM.read_user_tick_numbers(user) # ns[1] > ns[0]
        if ns[0] > AMM.active_band():  # We are not in liquidation mode
            p: uint256 = AMM.price_oracle()
            p_up: uint256 = AMM.p_oracle_up(ns[0])
            if p > p_up:
                health += convert(unsafe_div((p - p_up) * AMM.get_sum_xy(user)[1] * COLLATERAL_PRECISION, debt), int256)

    return health


@external
@view
@nonreentrant('lock')
def health_calculator(user: address, d_collateral: int256, d_debt: int256, full: bool, N: uint256 = 0) -> int256:
    """
    @notice Health predictor in case user changes the debt or collateral
    @param user Address of the user
    @param d_collateral Change in collateral amount (signed)
    @param d_debt Change in debt amount (signed)
    @param full Whether it's a 'full' health or not
    @param N Number of bands in case loan doesn't yet exist
    @return Signed health value
    """
    xy: uint256[2] = AMM.get_sum_xy(user)
    xy[1] *= COLLATERAL_PRECISION
    ns: int256[2] = AMM.read_user_tick_numbers(user)
    debt: int256 = convert(self._debt_ro(user), int256)
    n: uint256 = N
    ld: int256 = 0
    if debt != 0:
        ld = convert(self.liquidation_discounts[user], int256)
        n = convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256)
    else:
        ld = convert(self.liquidation_discount, int256)
        ns[0] = max_value(int256)  # This will trigger a "re-deposit"

    n1: int256 = 0
    collateral: int256 = 0
    x_eff: int256 = 0
    debt += d_debt
    assert debt > 0, "Non-positive debt"

    active_band: int256 = AMM.active_band_with_skip()

    if ns[0] > active_band and (d_collateral != 0 or d_debt != 0):  # re-deposit
        collateral = convert(xy[1], int256) + d_collateral
        n1 = self._calculate_debt_n1(convert(collateral, uint256), convert(debt, uint256), n)

    else:
        n1 = ns[0]
        x_eff = convert(AMM.get_x_down(user) * 10**18, int256)

    p0: int256 = convert(AMM.p_oracle_up(n1), int256)
    if ns[0] > active_band:
        x_eff = convert(self.get_y_effective(convert(collateral, uint256), n, 0), int256) * p0

    health: int256 = unsafe_div(x_eff, debt)
    health = health - unsafe_div(health * ld, 10**18) - 10**18

    if full:
        if n1 > active_band:  # We are not in liquidation mode
            p_diff: int256 = max(p0, convert(AMM.price_oracle(), int256)) - p0
            if p_diff > 0:
                health += unsafe_div(p_diff * collateral, debt)

    return health


@internal
@view
def _get_f_remove(frac: uint256, health_limit: uint256) -> uint256:
    # f_remove = ((1 + h / 2) / (1 + h) * (1 - frac) + frac) * frac
    f_remove: uint256 = 10 ** 18
    if frac < 10 ** 18:
        f_remove = unsafe_div(unsafe_mul(unsafe_add(10 ** 18, unsafe_div(health_limit, 2)), unsafe_sub(10 ** 18, frac)), unsafe_add(10 ** 18, health_limit))
        f_remove = unsafe_div(unsafe_mul(unsafe_add(f_remove, frac), frac), 10 ** 18)

    return f_remove

@internal
def _liquidate(user: address, min_x: uint256, health_limit: uint256, frac: uint256, use_eth: bool,
               callbacker: address, callback_args: DynArray[uint256,5]):
    """
    @notice Perform a bad liquidation of user if the health is too bad
    @param user Address of the user
    @param min_x Minimal amount of stablecoin withdrawn (to avoid liquidators being sandwiched)
    @param health_limit Minimal health to liquidate at
    @param frac Fraction to liquidate; 100% = 10**18
    @param use_eth Use wrapping/unwrapping if collateral is ETH
    @param callbacker Address of the callback contract
    @param callback_args Extra arguments for the callback (up to 5) such as min_amount etc
    """
    debt: uint256 = 0
    rate_mul: uint256 = 0
    debt, rate_mul = self._debt(user)

    if health_limit != 0:
        assert self._health(user, debt, True, health_limit) < 0, "Not enough rekt"

    final_debt: uint256 = debt
    debt = unsafe_div(debt * frac, 10**18)
    assert debt > 0
    final_debt = unsafe_sub(final_debt, debt)

    # Withdraw sender's stablecoin and collateral to our contract
    # When frac is set - we withdraw a bit less for the same debt fraction
    # f_remove = ((1 + h/2) / (1 + h) * (1 - frac) + frac) * frac
    # where h is health limit.
    # This is less than full h discount but more than no discount
    f_remove: uint256 = self._get_f_remove(frac, health_limit)
    xy: uint256[2] = AMM.withdraw(user, f_remove)  # [stable, collateral]

    # x increase in same block -> price up -> good
    # x decrease in same block -> price down -> bad
    assert xy[0] >= min_x, "Slippage"

    min_amm_burn: uint256 = min(xy[0], debt)
    if min_amm_burn != 0:
        STABLECOIN.transferFrom(AMM.address, self, min_amm_burn)

    if debt > xy[0]:
        to_repay: uint256 = unsafe_sub(debt, xy[0])

        if callbacker == empty(address):
            # Withdraw collateral if no callback is present
            self._withdraw_collateral(msg.sender, xy[1], use_eth)
            # Request what's left from user
            STABLECOIN.transferFrom(msg.sender, self, to_repay)

        else:
            # Move collateral to callbacker, call it and remove everything from it back in
            if xy[1] > 0:
                assert COLLATERAL_TOKEN.transferFrom(AMM.address, callbacker, xy[1], default_return_value=True)
            # Callback
            cb: CallbackData = self.execute_callback(
                callbacker, CALLBACK_LIQUIDATE, user, xy[0], xy[1], debt, callback_args)
            assert cb.stablecoins >= to_repay, "not enough proceeds"
            if cb.stablecoins > to_repay:
                STABLECOIN.transferFrom(callbacker, msg.sender, unsafe_sub(cb.stablecoins, to_repay))
            STABLECOIN.transferFrom(callbacker, self, to_repay)
            if cb.collateral > 0:
                assert COLLATERAL_TOKEN.transferFrom(callbacker, msg.sender, cb.collateral)

    else:
        # Withdraw collateral
        self._withdraw_collateral(msg.sender, xy[1], use_eth)
        # Return what's left to user
        if xy[0] > debt:
            STABLECOIN.transferFrom(AMM.address, msg.sender, unsafe_sub(xy[0], debt))

    self.redeemed += debt
    self.loan[user] = Loan({initial_debt: final_debt, rate_mul: rate_mul})
    log Repay(user, xy[1], debt)
    log Liquidate(msg.sender, user, xy[1], xy[0], debt)
    if final_debt == 0:
        log UserState(user, 0, 0, 0, 0, 0)  # Not logging partial removeal b/c we have not enough info
        self._remove_from_list(user)

    d: uint256 = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul
    self._total_debt.initial_debt = unsafe_sub(max(d, debt), debt)
    self._total_debt.rate_mul = rate_mul


@external
@nonreentrant('lock')
def liquidate(user: address, min_x: uint256, use_eth: bool = True):
    """
    @notice Peform a bad liquidation (or self-liquidation) of user if health is not good
    @param min_x Minimal amount of stablecoin to receive (to avoid liquidators being sandwiched)
    @param use_eth Use wrapping/unwrapping if collateral is ETH
    """
    discount: uint256 = 0
    if user != msg.sender:
        discount = self.liquidation_discounts[user]
    self._liquidate(user, min_x, discount, 10**18, use_eth, empty(address), [])


@external
@nonreentrant('lock')
def liquidate_extended(user: address, min_x: uint256, frac: uint256, use_eth: bool,
                       callbacker: address, callback_args: DynArray[uint256,5]):
    """
    @notice Peform a bad liquidation (or self-liquidation) of user if health is not good
    @param min_x Minimal amount of stablecoin to receive (to avoid liquidators being sandwiched)
    @param frac Fraction to liquidate; 100% = 10**18
    @param use_eth Use wrapping/unwrapping if collateral is ETH
    @param callbacker Address of the callback contract
    @param callback_args Extra arguments for the callback (up to 5) such as min_amount etc
    """
    discount: uint256 = 0
    if user != msg.sender:
        discount = self.liquidation_discounts[user]
    self._liquidate(user, min_x, discount, min(frac, 10**18), use_eth, callbacker, callback_args)


@view
@external
@nonreentrant('lock')
def tokens_to_liquidate(user: address, frac: uint256 = 10 ** 18) -> uint256:
    """
    @notice Calculate the amount of stablecoins to have in liquidator's wallet to liquidate a user
    @param user Address of the user to liquidate
    @param frac Fraction to liquidate; 100% = 10**18
    @return The amount of stablecoins needed
    """
    health_limit: uint256 = 0
    if user != msg.sender:
        health_limit = self.liquidation_discounts[user]
    f_remove: uint256 = self._get_f_remove(frac, health_limit)
    stablecoins: uint256 = unsafe_div(AMM.get_sum_xy(user)[0] * f_remove, 10 ** 18)
    debt: uint256 = unsafe_div(self._debt_ro(user) * frac, 10 ** 18)

    return unsafe_sub(max(debt, stablecoins), stablecoins)


@view
@external
@nonreentrant('lock')
def health(user: address, full: bool = False) -> int256:
    """
    @notice Returns position health normalized to 1e18 for the user.
            Liquidation starts when < 0, however devaluation of collateral doesn't cause liquidation
    """
    return self._health(user, self._debt_ro(user), full, self.liquidation_discounts[user])


@view
@external
@nonreentrant('lock')
def users_to_liquidate(_from: uint256=0, _limit: uint256=0) -> DynArray[Position, 1000]:
    """
    @notice Returns a dynamic array of users who can be "hard-liquidated".
            This method is designed for convenience of liquidation bots.
    @param _from Loan index to start iteration from
    @param _limit Number of loans to look over
    @return Dynamic array with detailed info about positions of users
    """
    n_loans: uint256 = self.n_loans
    limit: uint256 = _limit
    if _limit == 0:
        limit = n_loans
    ix: uint256 = _from
    out: DynArray[Position, 1000] = []
    for i in range(10**6):
        if ix >= n_loans or i == limit:
            break
        user: address = self.loans[ix]
        debt: uint256 = self._debt_ro(user)
        health: int256 = self._health(user, debt, True, self.liquidation_discounts[user])
        if health < 0:
            xy: uint256[2] = AMM.get_sum_xy(user)
            out.append(Position({
                user: user,
                x: xy[0],
                y: xy[1],
                debt: debt,
                health: health
            }))
        ix += 1
    return out


# AMM has a nonreentrant decorator
@view
@external
def amm_price() -> uint256:
    """
    @notice Current price from the AMM
    """
    return AMM.get_p()


@view
@external
@nonreentrant('lock')
def user_prices(user: address) -> uint256[2]:  # Upper, lower
    """
    @notice Lowest price of the lower band and highest price of the upper band the user has deposit in the AMM
    @param user User address
    @return (upper_price, lower_price)
    """
    assert AMM.has_liquidity(user)
    ns: int256[2] = AMM.read_user_tick_numbers(user) # ns[1] > ns[0]
    return [AMM.p_oracle_up(ns[0]), AMM.p_oracle_down(ns[1])]


@view
@external
@nonreentrant('lock')
def user_state(user: address) -> uint256[4]:
    """
    @notice Return the user state in one call
    @param user User to return the state for
    @return (collateral, stablecoin, debt, N)
    """
    xy: uint256[2] = AMM.get_sum_xy(user)
    ns: int256[2] = AMM.read_user_tick_numbers(user) # ns[1] > ns[0]
    return [xy[1], xy[0], self._debt_ro(user), convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256)]


# AMM has nonreentrant decorator
@external
def set_amm_fee(fee: uint256):
    """
    @notice Set the AMM fee (factory admin only)
    @param fee The fee which should be no higher than MAX_FEE
    """
    assert msg.sender == FACTORY.admin()
    assert fee <= MAX_FEE and fee >= MIN_FEE, "Fee"
    AMM.set_fee(fee)


# AMM has nonreentrant decorator
@external
def set_amm_admin_fee(fee: uint256):
    """
    @notice Set AMM's admin fee
    @param fee New admin fee (not higher than MAX_ADMIN_FEE)
    """
    assert msg.sender == FACTORY.admin()
    assert fee <= MAX_ADMIN_FEE, "High fee"
    AMM.set_admin_fee(fee)


@nonreentrant('lock')
@external
def set_monetary_policy(monetary_policy: address):
    """
    @notice Set monetary policy contract
    @param monetary_policy Address of the monetary policy contract
    """
    assert msg.sender == FACTORY.admin()
    self.monetary_policy = MonetaryPolicy(monetary_policy)
    MonetaryPolicy(monetary_policy).rate_write()
    log SetMonetaryPolicy(monetary_policy)


@nonreentrant('lock')
@external
def set_borrowing_discounts(loan_discount: uint256, liquidation_discount: uint256):
    """
    @notice Set discounts at which we can borrow (defines max LTV) and where bad liquidation starts
    @param loan_discount Discount which defines LTV
    @param liquidation_discount Discount where bad liquidation starts
    """
    assert msg.sender == FACTORY.admin()
    assert loan_discount > liquidation_discount
    assert liquidation_discount >= MIN_LIQUIDATION_DISCOUNT
    assert loan_discount <= MAX_LOAN_DISCOUNT
    self.liquidation_discount = liquidation_discount
    self.loan_discount = loan_discount
    log SetBorrowingDiscounts(loan_discount, liquidation_discount)


@external
@nonreentrant('lock')
def set_callback(cb: address):
    """
    @notice Set liquidity mining callback
    """
    assert msg.sender == FACTORY.admin()
    AMM.set_callback(cb)


@external
@view
def admin_fees() -> uint256:
    """
    @notice Calculate the amount of fees obtained from the interest
    """
    rate_mul: uint256 = AMM.get_rate_mul()
    loan: Loan = self._total_debt
    loan.initial_debt = loan.initial_debt * rate_mul / loan.rate_mul
    loan.initial_debt += self.redeemed
    minted: uint256 = self.minted
    return unsafe_sub(max(loan.initial_debt, minted), minted)


@external
@nonreentrant('lock')
def collect_fees() -> uint256:
    """
    @notice Collect the fees charged as interest
    """
    _to: address = FACTORY.fee_receiver()
    # AMM-based fees
    borrowed_fees: uint256 = AMM.admin_fees_x()
    collateral_fees: uint256 = AMM.admin_fees_y()
    if borrowed_fees > 0:
        STABLECOIN.transferFrom(AMM.address, _to, borrowed_fees)
    if collateral_fees > 0:
        assert COLLATERAL_TOKEN.transferFrom(AMM.address, _to, collateral_fees, default_return_value=True)
    AMM.reset_admin_fees()

    # Borrowing-based fees
    rate_mul: uint256 = self._rate_mul_w()
    loan: Loan = self._total_debt
    loan.initial_debt = loan.initial_debt * rate_mul / loan.rate_mul
    loan.rate_mul = rate_mul
    self._total_debt = loan

    # Amount which would have been redeemed if all the debt was repaid now
    to_be_redeemed: uint256 = loan.initial_debt + self.redeemed
    # Amount which was minted when borrowing + all previously claimed admin fees
    minted: uint256 = self.minted
    # Difference between to_be_redeemed and minted amount is exactly due to interest charged
    if to_be_redeemed > minted:
        self.minted = to_be_redeemed
        to_be_redeemed = unsafe_sub(to_be_redeemed, minted)  # Now this is the fees to charge
        STABLECOIN.transfer(_to, to_be_redeemed)
        log CollectFees(to_be_redeemed, loan.initial_debt)
        return to_be_redeemed
    else:
        log CollectFees(0, loan.initial_debt)
        return 0

Contract Security Audit

Contract ABI

[{"name":"UserState","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral","type":"uint256","indexed":false},{"name":"debt","type":"uint256","indexed":false},{"name":"n1","type":"int256","indexed":false},{"name":"n2","type":"int256","indexed":false},{"name":"liquidation_discount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Borrow","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral_increase","type":"uint256","indexed":false},{"name":"loan_increase","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Repay","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral_decrease","type":"uint256","indexed":false},{"name":"loan_decrease","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveCollateral","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral_decrease","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Liquidate","inputs":[{"name":"liquidator","type":"address","indexed":true},{"name":"user","type":"address","indexed":true},{"name":"collateral_received","type":"uint256","indexed":false},{"name":"stablecoin_received","type":"uint256","indexed":false},{"name":"debt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetMonetaryPolicy","inputs":[{"name":"monetary_policy","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetBorrowingDiscounts","inputs":[{"name":"loan_discount","type":"uint256","indexed":false},{"name":"liquidation_discount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"CollectFees","inputs":[{"name":"amount","type":"uint256","indexed":false},{"name":"new_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"collateral_token","type":"address"},{"name":"monetary_policy","type":"address"},{"name":"loan_discount","type":"uint256"},{"name":"liquidation_discount","type":"uint256"},{"name":"amm","type":"address"}],"outputs":[]},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"view","type":"function","name":"factory","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"amm","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"collateral_token","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"debt","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"loan_exists","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"total_debt","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"max_borrowable","inputs":[{"name":"collateral","type":"uint256"},{"name":"N","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"min_collateral","inputs":[{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calculate_debt_n1","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"}],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"payable","type":"function","name":"create_loan","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"create_loan_extended","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"},{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"add_collateral","inputs":[{"name":"collateral","type":"uint256"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"add_collateral","inputs":[{"name":"collateral","type":"uint256"},{"name":"_for","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"remove_collateral","inputs":[{"name":"collateral","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"remove_collateral","inputs":[{"name":"collateral","type":"uint256"},{"name":"use_eth","type":"bool"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"borrow_more","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"repay","inputs":[{"name":"_d_debt","type":"uint256"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"repay","inputs":[{"name":"_d_debt","type":"uint256"},{"name":"_for","type":"address"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"repay","inputs":[{"name":"_d_debt","type":"uint256"},{"name":"_for","type":"address"},{"name":"max_active_band","type":"int256"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"repay","inputs":[{"name":"_d_debt","type":"uint256"},{"name":"_for","type":"address"},{"name":"max_active_band","type":"int256"},{"name":"use_eth","type":"bool"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"repay_extended","inputs":[{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"health_calculator","inputs":[{"name":"user","type":"address"},{"name":"d_collateral","type":"int256"},{"name":"d_debt","type":"int256"},{"name":"full","type":"bool"}],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"view","type":"function","name":"health_calculator","inputs":[{"name":"user","type":"address"},{"name":"d_collateral","type":"int256"},{"name":"d_debt","type":"int256"},{"name":"full","type":"bool"},{"name":"N","type":"uint256"}],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"nonpayable","type":"function","name":"liquidate","inputs":[{"name":"user","type":"address"},{"name":"min_x","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"liquidate","inputs":[{"name":"user","type":"address"},{"name":"min_x","type":"uint256"},{"name":"use_eth","type":"bool"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"liquidate_extended","inputs":[{"name":"user","type":"address"},{"name":"min_x","type":"uint256"},{"name":"frac","type":"uint256"},{"name":"use_eth","type":"bool"},{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"tokens_to_liquidate","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"tokens_to_liquidate","inputs":[{"name":"user","type":"address"},{"name":"frac","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"health","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"view","type":"function","name":"health","inputs":[{"name":"user","type":"address"},{"name":"full","type":"bool"}],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"view","type":"function","name":"users_to_liquidate","inputs":[],"outputs":[{"name":"","type":"tuple[]","components":[{"name":"user","type":"address"},{"name":"x","type":"uint256"},{"name":"y","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"health","type":"int256"}]}]},{"stateMutability":"view","type":"function","name":"users_to_liquidate","inputs":[{"name":"_from","type":"uint256"}],"outputs":[{"name":"","type":"tuple[]","components":[{"name":"user","type":"address"},{"name":"x","type":"uint256"},{"name":"y","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"health","type":"int256"}]}]},{"stateMutability":"view","type":"function","name":"users_to_liquidate","inputs":[{"name":"_from","type":"uint256"},{"name":"_limit","type":"uint256"}],"outputs":[{"name":"","type":"tuple[]","components":[{"name":"user","type":"address"},{"name":"x","type":"uint256"},{"name":"y","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"health","type":"int256"}]}]},{"stateMutability":"view","type":"function","name":"amm_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"user_prices","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"view","type":"function","name":"user_state","inputs":[{"name":"user","type":"address"}],"outputs":[{"name":"","type":"uint256[4]"}]},{"stateMutability":"nonpayable","type":"function","name":"set_amm_fee","inputs":[{"name":"fee","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_amm_admin_fee","inputs":[{"name":"fee","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_monetary_policy","inputs":[{"name":"monetary_policy","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_borrowing_discounts","inputs":[{"name":"loan_discount","type":"uint256"},{"name":"liquidation_discount","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_callback","inputs":[{"name":"cb","type":"address"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"admin_fees","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"collect_fees","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"liquidation_discounts","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"loans","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"loan_ix","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"n_loans","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"minted","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"redeemed","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"monetary_policy","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"liquidation_discount","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"loan_discount","inputs":[],"outputs":[{"name":"","type":"uint256"}]}]

6020615ff36000396000518060a01c615fee576101205260206160136000396000518060a01c615fee576101405260206160736000396000518060a01c615fee576101605234615fee5733615a71523363e9cbd8226101a05260206101a060046101bc845afa610074573d600060003e3d6000fd5b60203d10615fee576101a0518060a01c615fee576101e0526101e09050516101805261018051615a915260126101805163313ce5676101a05260206101a060046101bc845afa6100c9573d600060003e3d6000fd5b60203d10615fee576101a090505118615fee5761014051680100000000000000095560206160536000396000516801000000000000000a5560206160336000396000516801000000000000000b55670de0b6b3a764000060045561016051615af1526101605163f446c1d06101c05260206101c060046101dc845afa610154573d600060003e3d6000fd5b60203d10615fee576101c09050516101a0526101a051615b11526101a05160018103818111615fee579050615b31526101a051670de0b6b3a7640000810281670de0b6b3a7640000820418615fee57905060016101a051038015615fee57808204905090506040526101c76101c0615e88565b6101c051615b515261012051615ab1526101205163313ce5676101c05260206101c060046101dc845afa610200573d600060003e3d6000fd5b60203d10615fee576101c09050518060120360128111615fee579050600a0a615ad15260016101a051036101a0516ec097ce7bc90715b34b9f10000000008102816ec097ce7bc90715b34b9f1000000000820418615fee579050048060b5710100000000000000000000000000000000008210610284578160801c91508060401b90505b690100000000000000000082106102a2578160401c91508060201b90505b6501000000000082106102bc578160201c91508060101b90505b630100000082106102d4578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050615b71526101805163095ea7b36101c052336101e0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102005260206101c060446101dc6000855af1610394573d600060003e3d6000fd5b60203d10615fee576101c0518060011c615fee57610220526102205050610120513363ad5c46486101c05260206101c060046101dc845afa6103db573d600060003e3d6000fd5b60203d10615fee576101c0518060a01c615fee576102005261020090505118610405576001615b91525b615a7161041761000039615bb1610000f36003361161000c576133c5565b60003560e01c6323cfed0381186100565760643610615a5f57600054600214615a5f576002600055346102c052606060046102e03760016103405261004f614527565b6003600055005b63bc61ea2381186102575760c43610615a5f576064358060a01c615a5f57610640526084356004016005813511615a5f5780358061066052602082018160051b80826106803750505050600054600214615a5f5760026000556020615a9160003960005163a9059cbb610720526106405161074052602435610760526020610720604461073c6000855af16100f0573d600060003e3d6000fd5b60203d10615a5f57610720518060011c615a5f57610780526107805050610640516040527fe62214fe0000000000000000000000000000000000000000000000000000000060605233608052600060a0526040600460c0376106605180610100528060051b806101208261068060045afa50505061016f6107406141d5565b61074060408101905051610720526004356040523460605261018f613efa565b6020615ab16000396000516323b872dd6107405261064051610760526020615af160003960005161078052610720516107a0526020610740606461075c6000855af16101e0573d600060003e3d6000fd5b3d6101f757803b15615a5f5760016107c052610210565b60203d10615a5f57610740518060011c615a5f576107c0525b6107c090505115615a5f5760006102c05260043561072051808201828110615a5f57905090506102e0526040602461030037600061034052610250614527565b6003600055005b636f972f1281186102745760243610615a5f573361052052610297565b6324049e5781186102ee5760443610615a5f576024358060a01c615a5f57610520525b600054600214615a5f5760026000556004356102b2576102e7565b6004356102c05260006102e05261052051610300526000610320526102d561495f565b600435604052346060526102e7613efa565b6003600055005b63dd171e7c81186103db5760443610615a5f57600054600214615a5f57600260005560243561031c576103d4565b604060046102c037336103005260006103205261033761495f565b600435156103515760043560405234606052610351613efa565b6020615a9160003960005163a9059cbb610520523361054052602435610560526020610520604461053c6000855af161038f573d600060003e3d6000fd5b60203d10615a5f57610520518060011c615a5f576105805261058050506801000000000000000754602435808201828110615a5f579050905068010000000000000007555b6003600055005b63371fd8e681186104235760243610615a5f57336102c0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102e0526001610300526104e2565b63acb7081581186104755760443610615a5f576024358060a01c615a5f576102c0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102e0526001610300526104e2565b63b4440df481186104a95760643610615a5f576024358060a01c615a5f576102c0526044356102e0526001610300526104e2565b6337671f938118610be25760843610615a5f576024358060a01c615a5f576102c0526044356102e0526064358060011c615a5f57610300525b600054600214615a5f5760026000556004356104fd57610bdb565b604036610320376102c05160a0526105166103606135e4565b6103608051610320526020810151610340525061032051610597576012610360527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b6103205160043580828118828410021890509050610360526103605161032051036103205261032051610750576020615af160003960005163f3fef3a36103c0526102c0516103e052670de0b6b3a76400006104005260406103c060446103dc6000855af161060b573d600060003e3d6000fd5b60403d10615a5f576103c0905080516103805260208101516103a0525061038051156106aa57336102c05118615a5f576020615a916000396000516323b872dd6103c0526020615af16000396000516103e0526102c05161040052610380516104205260206103c060646103dc6000855af161068c573d600060003e3d6000fd5b60203d10615a5f576103c0518060011c615a5f576104405261044050505b6103a051156106d0576102c0516040526103a051606052610300516080526106d061405b565b6102c0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366103c03760a06103c0a26102c0517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d06103a0516103c052610360516103e05260406103c0a26102c051604052610add614dae56610add565b6020615af160003960005163c16ef2646103a05260206103a060046103bc845afa610780573d600060003e3d6000fd5b60203d10615a5f576103a0905051610380526102e0516103805113615a5f576020615af160003960005163b461100d6103e0526102c0516104005260406103e060246103fc845afa6107d7573d600060003e3d6000fd5b60403d10615a5f576103e0905080516103a05260208101516103c0525060016103a0516103c051030160008112615a5f576103e052600061040052610380516103a051136108e35760026102c051602052600052604060002054610400526102c0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6104205261032051610440526103a051610460526103c05161048052610400516104a05260a0610420a26102c0517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d060006104205261036051610440526040610420a2610aa2565b6020615af160003960005163f3fef3a3610460526102c05161048052670de0b6b3a76400006104a0526040610460604461047c6000855af161092a573d600060003e3d6000fd5b60403d10615a5f57610460905080516104205260208101516104405250610440516101205261032051610140526103e0516101605261096a610480613830565b6104805161046052610460516103a0516103c051038082018281126000831218615a5f5790509050610480526020615af160003960005163ab047e006104a0526102c0516104c052610440516104e05261046051610500526104805161052052803b15615a5f5760006104a060846104bc6000855af16109ef573d600060003e3d6000fd5b506801000000000000000a54610400526104005160026102c0516020526000526040600020556102c0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610440516104a052610320516104c052610460516104e0526104805161050052610400516105205260a06104a0a26102c0517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d060006104a052610360516104c05260406104a0a25b336102c05114610add5760016102c0516040526103205160605260006080526104005160a052610ad3610420614e89565b6104205112615a5f575b6020615a916000396000516323b872dd61038052336103a052306103c052610360516103e0526020610380606461039c6000855af1610b21573d600060003e3d6000fd5b60203d10615a5f57610380518060011c615a5f57610400526104005050680100000000000000085461036051808201828110615a5f5790509050680100000000000000085560016102c05160205260005260406000206103205181556103405160018201555060035461034051808202811583838304141715615a5f57905090506004548015615a5f5780820490509050610380526103605161038051610360518082811882841102189050905003600355610340516004555b6003600055005b63c45a01558118610c0e5760043610615a5f5734615a5f576020615a7160003960005160405260206040f35b632a9439458118610c3a5760043610615a5f5734615a5f576020615af160003960005160405260206040f35b632621db2f8118610c665760043610615a5f5734615a5f576020615ab160003960005160405260206040f35b639b6c56ec8118610cac5760243610615a5f576004358060a01c615a5f5760c05234615a5f57600054600214615a5f57602060c051604052610ca860e0613669565b60e0f35b63a21adb9e8118610cf85760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f576001604051602052600052604060002054151560605260206060f35b6331dc3ca88118610d8c5760043610615a5f5734615a5f576020615af160003960005163095a0fc6606052602060606004607c845afa610d3d573d600060003e3d6000fd5b60203d10615a5f576060905051604052600354606052600454608052606051604051808202811583838304141715615a5f57905090506080518015615a5f578082049050905060a052602060a0f35b639a4971968118610ece5760443610615a5f5734615a5f57600054600214615a5f576004356020615ad1600039600051808202811583838304141715615a5f57905090506040526024356060526801000000000000000b54608052610df2610200613709565b610200516101e0526001670de0b6b3a76400006101e051610e14610220613cef565b61022051808202811583838304141715615a5f579050905004600181811860018311021890500361020052670de0b6b3a764000061020051670de05bc096e9c000810281670de05bc096e9c000820418615a5f5790500461020052610200516020615a916000396000516370a082316102205230610240526020610220602461023c845afa610ea8573d600060003e3d6000fd5b60203d10615a5f5761022090505180828118828410021890509050610260526020610260f35b63a75732068118610ffc5760443610615a5f5734615a5f57600054600214615a5f57670de05bc096e9c0006020615ad1600039600051600435670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f579050610f306101e0613cef565b6101e0518015615a5f5780820490509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f579050670de0b6b3a76400006040526024356060526801000000000000000b54608052610f8a610200613709565b610200518015615a5f57808204905090506024356024356107d08101818110615a5f579050808202811583838304141715615a5f5790509050808201828110615a5f579050905004670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f57905004610220526020610220f35b63720fb25481186110385760643610615a5f5734615a5f57600054600214615a5f57602060606004610120376110336102c0613830565b6102c0f35b63d14ff5b681186110565760243610615a5f57600161052052611079565b632e4af52a81186110d95760443610615a5f576024358060011c615a5f57610520525b34615a5f57600054600214615a5f576002600055600435611099576110d2565b6004356102c05260006102e05233610300526001610320526110b961495f565b33604052600435606052610520516080526110d261405b565b6003600055005b63152f65cb81186118a65760643610615a5f576004358060a01c615a5f57610640526024356004016005813511615a5f5780358061066052602082018160051b8082610680375050505034615a5f57600054600214615a5f5760026000556020615af160003960005163b461100d6107605233610780526040610760602461077c845afa61116c573d600060003e3d6000fd5b60403d10615a5f576107609050805161072052602081015161074052506020615af160003960005163f3fef3a36107a052336107c052670de0b6b3a76400006107e05260406107a060446107bc6000855af16111cd573d600060003e3d6000fd5b60403d10615a5f576107a09050805161076052602081015161078052506040366107a0373360a0526112006107e06135e4565b6107e080516107a05260208101516107c052506020615ab16000396000516323b872dd6107e0526020615af1600039600051610800526106405161082052610780516108405260206107e060646107fc6000855af1611264573d600060003e3d6000fd5b3d61127b57803b15615a5f57600161086052611294565b60203d10615a5f576107e0518060011c615a5f57610860525b6108605050610640516040527fef67dc7400000000000000000000000000000000000000000000000000000000606052336080526107605160a0526107805160c0526107a05160e0526106605180610100528060051b806101208261068060045afa5050506113046108406141d5565b61084080516107e052602081015161080052604081015161082052506108005161076051808201828110615a5f5790509050610840526108405115615a5f576000610860526107a0516108405110156115a55760016107205161074051030160008112615a5f57610880526107e051610720511315615a5f576108005161086052610800516107a051036107a05261082051610120526107a0516101405261088051610160526113b56108c0613830565b6108c0516108a0526108a0516107205161074051038082018281126000831218615a5f57905090506108c0526020615af160003960005163ab047e006108e052336109005261082051610920526108a051610940526108c05161096052803b15615a5f5760006108e060846108fc6000855af1611437573d600060003e3d6000fd5b506801000000000000000a546108e0526108e0516002336020526000526040600020556020615ab16000396000516323b872dd6109005261064051610920526020615af16000396000516109405261082051610960526020610900606461091c6000855af16114ab573d600060003e3d6000fd5b3d6114c257803b15615a5f576001610980526114db565b60203d10615a5f57610900518060011c615a5f57610980525b61098090505115615a5f576020615a916000396000516323b872dd610900526106405161092052306109405261080051610960526020610900606461091c6000855af161152d573d600060003e3d6000fd5b60203d10615a5f57610900518060011c615a5f57610980526109805050337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261082051610900526107a051610920526108a051610940526108c051610960526108e0516109805260a0610900a26000610780526117ce565b6107a0516108605260006107a052336040526115bf614dae565b610800511561162d576020615a916000396000516323b872dd61088052610640516108a052306108c052610800516108e0526020610880606461089c6000855af161160f573d600060003e3d6000fd5b60203d10615a5f57610880518060011c615a5f576109005261090050505b61076051156116a2576020615a916000396000516323b872dd610880526020615af16000396000516108a052306108c052610760516108e0526020610880606461089c6000855af1611684573d600060003e3d6000fd5b60203d10615a5f57610880518060011c615a5f576109005261090050505b61086051610840511115611712576020615a9160003960005163a9059cbb61088052336108a0526108605161084051036108c0526020610880604461089c6000855af16116f4573d600060003e3d6000fd5b60203d10615a5f57610880518060011c615a5f576108e0526108e050505b610820511561179e576020615ab16000396000516323b872dd61088052610640516108a052336108c052610820516108e0526020610880606461089c6000855af1611762573d600060003e3d6000fd5b3d61177957803b15615a5f57600161090052611792565b60203d10615a5f57610880518060011c615a5f57610900525b61090090505115615a5f575b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366108803760a0610880a25b337f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d06107805161088052610860516108a0526040610880a2680100000000000000085461086051808201828110615a5f5790509050680100000000000000085560013360205260005260406000206107a05181556107c0516001820155506003546107c051808202811583838304141715615a5f57905090506004548015615a5f57808204905090506108805261086051610880516108605180828118828411021890509050036003556107c0516004556003600055005b630b8db68181186118c45760843610615a5f576000610300526118df565b6322c714538118611e865760a43610615a5f57608435610300525b6004358060a01c615a5f576102c0526064358060011c615a5f576102e05234615a5f57600054600214615a5f576020615af160003960005163544fb5c1610360526102c051610380526040610360602461037c845afa611944573d600060003e3d6000fd5b60403d10615a5f57610360905080516103205260208101516103405250610340516020615ad1600039600051808202811583838304141715615a5f5790509050610340526020615af160003960005163b461100d6103a0526102c0516103c05260406103a060246103bc845afa6119c0573d600060003e3d6000fd5b60403d10615a5f576103a09050805161036052602081015161038052506102c0516040526119ef6103c0613669565b6103c0518060ff1c615a5f576103a052610300516103c05260006103e0526103a05115611a515760026102c0516020526000526040600020548060ff1c615a5f576103e05260016103605161038051030160008112615a5f576103c052611a8e565b6801000000000000000a548060ff1c615a5f576103e0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610360525b606036610400376103a0516044358082018281126000831218615a5f57905090506103a05260016103a0511215611b25576011610460527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104805261046050610460518061048001601f826000031636823750506308c379a061042052602061044052601f19601f61046051011660440161043cfd5b6020615af160003960005163c16ef264610480526020610480600461049c845afa611b55573d600060003e3d6000fd5b60203d10615a5f5761048090505161046052610460516103605113611b7b576000611b90565b60243515611b8a576001611b90565b60443515155b611c115761036051610400526020615af16000396000516362ca4b18610480526102c0516104a0526020610480602461049c845afa611bd4573d600060003e3d6000fd5b60203d10615a5f57610480905051670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f5790508060ff1c615a5f5761044052611c73565b610340518060ff1c615a5f576024358082018281126000831218615a5f5790509050610420526104205160008112615a5f57610120526103a05160008112615a5f57610140526103c05161016052611c6a610480613830565b61048051610400525b6020615af1600039600051632eb858e76104a052610400516104c05260206104a060246104bc845afa611cab573d600060003e3d6000fd5b60203d10615a5f576104a09050518060ff1c615a5f576104805261046051610360511315611d30576104205160008112615a5f576040526103c0516060526000608052611cf96104a0613709565b6104a0518060ff1c615a5f576104805180820281191515600160ff1b8414151782158484840514171615615a5f5790509050610440525b6103a05161044051056104a0526104a051670de0b6b3a76400006104a0516103e05180820281191515600160ff1b8414151782158484840514171615615a5f5790509050058082038281136000831218615a5f5790509050670de0b6b3a76400008103818113615a5f5790506104a0526102e05115611e7f5761046051610400511315611e7f57610480516020615af16000396000516386fc88d36104e05260206104e060046104fc845afa611deb573d600060003e3d6000fd5b60203d10615a5f576104e09050518060ff1c615a5f5780828118828413021890509050610480518082038281136000831218615a5f57905090506104c05260016104c05112611e7f576104a0516103a0516104c0516104205180820281191515600160ff1b8414151782158484840514171615615a5f5790509050058082018281126000831218615a5f57905090506104a0525b60206104a0f35b63bcbaf4878118611ea45760443610615a5f5760016109e052611ec7565b633ecdb8288118611f535760643610615a5f576044358060011c615a5f576109e0525b6004358060a01c615a5f576109c05234615a5f57600054600214615a5f5760026000556000610a0052336109c05114611f115760026109c051602052600052604060002054610a00525b6109c0516106405260243561066052610a005161068052670de0b6b3a76400006106a0526109e0516106c0526040366106e037611f4c615221565b6003600055005b63036aed88811861206a5760e43610615a5f576004358060a01c615a5f576109c0526064358060011c615a5f576109e0526084358060a01c615a5f57610a005260a4356004016005813511615a5f57803580610a2052602082018160051b8082610a40375050505034615a5f57600054600214615a5f5760026000556000610ae052336109c05114611ff65760026109c051602052600052604060002054610ae0525b6109c0516106405260243561066052610ae05161068052604435670de0b6b3a7640000818118670de0b6b3a76400008310021890506106a0526109e0516106c052610a00516106e052610a205180610700528060051b8061072082610a4060045afa505050612063615221565b6003600055005b631b25cdaf811861208e5760243610615a5f57670de0b6b3a764000060e0526120a8565b63546e040d81186121d45760443610615a5f5760243560e0525b6004358060a01c615a5f5760c05234615a5f57600054600214615a5f576000610100523360c051146120ea57600260c051602052600052604060002054610100525b60e051604052610100516060526121026101406151b4565b6101405161012052670de0b6b3a76400006020615af160003960005163544fb5c16101605260c051610180526040610160602461017c845afa61214a573d600060003e3d6000fd5b60403d10615a5f5761016090505161012051808202811583838304141715615a5f57905090500461014052670de0b6b3a764000060c05160405261218f610180613669565b6101805160e051808202811583838304141715615a5f579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f35b63e2d8ebee81186121f25760243610615a5f57600061022052612215565b638908ea8281186122a15760443610615a5f576024358060011c615a5f57610220525b6004358060a01c615a5f576102005234615a5f57600054600214615a5f57602061020051610280526102005160405261224f610240613669565b610240516102a052610220516102c0526002610200516020526000526040600020546102e052610280516040526102a0516060526102c0516080526102e05160a05261229c610260614e89565b610260f35b627c98ab81186122bf5760043610615a5f5760403661020037612300565b6380e8f6ec81186122e45760243610615a5f5760043561020052600061022052612300565b6390f8667d81186125815760443610615a5f5760406004610200375b34615a5f57600054600214615a5f576801000000000000000654610240526102205161026052610220516123375761024051610260525b610200516102805260006102a0526000620f4240905b80620273c052610240516102805110156123715761026051620273c0511815612374565b60015b1561237e576124f0565b6102805167fffffffffffffffe8111615a5f5760050154620273e052620273e0516040526123ae62027420613669565b62027420516202740052620273e051604052620274005160605260016080526002620273e05160205260005260406000205460a0526123ef62027440614e89565b620274405162027420527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6202742051136124d0576020615af160003960005163544fb5c16202748052620273e051620274a05260406202748060246202749c845afa612461573d600060003e3d6000fd5b60403d10615a5f576202748090508051620274405260208101516202746052506102a0516103e78111615a5f57600181016102a05260a081026102c001620273e05181526202744051602082015262027460516040820152620274005160608201526202742051608082015250505b6102805160018101818110615a5f5790506102805260010181811861234d575b5050602080620273c05280620273c00160006102a05180835260a081026000826103e88111615a5f57801561256b57905b60a08102602087010160a082026102c00180518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050600101818118612521575b50508201602001915050905081019050620273c0f35b63d9f11a6481186125d65760043610615a5f5734615a5f5760206020615af160003960005163f2388acb604052602060406004605c845afa6125c8573d600060003e3d6000fd5b60203d10615a5f5760409050f35b632c5089c381186127365760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f576020615af160003960005163e8dd1ef1606052604051608052602060606024607c845afa612639573d600060003e3d6000fd5b60203d10615a5f576060518060011c615a5f5760a05260a090505115615a5f576020615af160003960005163b461100d60a05260405160c052604060a0602460bc845afa61268c573d600060003e3d6000fd5b60403d10615a5f5760a0905080516060526020810151608052506020615af1600039600051632eb858e760a05260605160c052602060a0602460bc845afa6126d9573d600060003e3d6000fd5b60203d10615a5f5760a0905051610120526020615af16000396000516324299b7a60e05260805161010052602060e0602460fc845afa61271e573d600060003e3d6000fd5b60203d10615a5f5760e0905051610140526040610120f35b63ec74d0a881186128545760243610615a5f576004358060a01c615a5f5760c05234615a5f57600054600214615a5f576020615af160003960005163544fb5c16101205260c051610140526040610120602461013c845afa61279d573d600060003e3d6000fd5b60403d10615a5f576101209050805160e052602081015161010052506020615af160003960005163b461100d6101605260c051610180526040610160602461017c845afa6127f0573d600060003e3d6000fd5b60403d10615a5f57610160905080516101205260208101516101405250610100516101805260e0516101a05260c05160405261282d610160613669565b610160516101c05260016101205161014051030160008112615a5f576101e0526080610180f35b634189617d811861297d5760243610615a5f5734615a5f576020615a7160003960005163f851a440604052602060406004605c845afa612899573d600060003e3d6000fd5b60203d10615a5f576040518060a01c615a5f5760805260809050513318615a5f5767016345785d8a000060043511156128d35760006128dd565b620f424060043510155b61293e5760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6020615af1600039600051631aa02d59604052600435606052803b15615a5f57600060406024605c6000855af161297a573d600060003e3d6000fd5b50005b63a5b4804a8118612a915760243610615a5f5734615a5f576020615a7160003960005163f851a440604052602060406004605c845afa6129c2573d600060003e3d6000fd5b60203d10615a5f576040518060a01c615a5f5760805260809050513318615a5f57670de0b6b3a76400006004351115612a525760086040527f486967682066656500000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6020615af1600039600051633217902f604052600435606052803b15615a5f57600060406024605c6000855af1612a8e573d600060003e3d6000fd5b50005b6381d2f1b78118612b885760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f5760026000556020615a7160003960005163f851a440606052602060606004607c845afa612af3573d600060003e3d6000fd5b60203d10615a5f576060518060a01c615a5f5760a05260a09050513318615a5f57604051680100000000000000095560405163e91f2f4c606052602060606004607c6000855af1612b49573d600060003e3d6000fd5b60203d10615a5f57606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a16003600055005b632a0c35868118612c7a5760443610615a5f5734615a5f57600054600214615a5f5760026000556020615a7160003960005163f851a440604052602060406004605c845afa612bdc573d600060003e3d6000fd5b60203d10615a5f576040518060a01c615a5f5760805260809050513318615a5f576024356004351115615a5f57662386f26fc1000060243510615a5f576706f05b59d3b2000060043511615a5f576024356801000000000000000a556004356801000000000000000b557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a16003600055005b63cc1891c78118612d415760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f5760026000556020615a7160003960005163f851a440606052602060606004607c845afa612cdc573d600060003e3d6000fd5b60203d10615a5f576060518060a01c615a5f5760a05260a09050513318615a5f576020615af160003960005163cc1891c7606052604051608052803b15615a5f57600060606024607c6000855af1612d39573d600060003e3d6000fd5b506003600055005b631b1800e38118612e1c5760043610615a5f5734615a5f576020615af160003960005163095a0fc6606052602060606004607c845afa612d86573d600060003e3d6000fd5b60203d10615a5f576060905051604052600354606052600454608052606051604051808202811583838304141715615a5f57905090506080518015615a5f57808204905090506060526060516801000000000000000854808201828110615a5f5790509050606052680100000000000000075460a05260a05160605160a051808281188284110218905090500360c052602060c0f35b631e0cfcef81186131fc5760043610615a5f5734615a5f57600054600214615a5f5760026000556020615a7160003960005163cab4d3db60c052602060c0600460dc845afa612e70573d600060003e3d6000fd5b60203d10615a5f5760c0518060a01c615a5f576101005261010090505160a0526020615af160003960005163d1fea73360e052602060e0600460fc845afa612ebd573d600060003e3d6000fd5b60203d10615a5f5760e090505160c0526020615af16000396000516389960ba7610100526020610100600461011c845afa612efd573d600060003e3d6000fd5b60203d10615a5f5761010090505160e05260c05115612f83576020615a916000396000516323b872dd610100526020615af16000396000516101205260a0516101405260c051610160526020610100606461011c6000855af1612f65573d600060003e3d6000fd5b60203d10615a5f57610100518060011c615a5f576101805261018050505b60e05115613016576020615ab16000396000516323b872dd610100526020615af16000396000516101205260a0516101405260e051610160526020610100606461011c6000855af1612fda573d600060003e3d6000fd5b3d612ff157803b15615a5f5760016101805261300a565b60203d10615a5f57610100518060011c615a5f57610180525b61018090505115615a5f575b6020615af160003960005163822fe50761010052803b15615a5f576000610100600461011c6000855af161304f573d600060003e3d6000fd5b5061305b610120613549565b610120516101005260035461012052600454610140526101205161010051808202811583838304141715615a5f5790509050610140518015615a5f57808204905090506101205261010051610140526101205160035561014051600455610120516801000000000000000854808201828110615a5f57905090506101605268010000000000000007546101805261018051610160511161313e577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f60006101a052610120516101c05260406101a0a160006101a05260206101a06131f5566131f5565b610160516801000000000000000755610180516101605103610160526020615a9160003960005163a9059cbb6101a05260a0516101c052610160516101e05260206101a060446101bc6000855af161319b573d600060003e3d6000fd5b60203d10615a5f576101a0518060011c615a5f576102005261020050507f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f610160516101a052610120516101c05260406101a0a160206101605b6003600055f35b635457ff7b811861323c5760243610615a5f576004358060a01c615a5f5760405234615a5f57600260405160205260005260406000205460605260206060f35b63e1ec3c6881186132735760243610615a5f5734615a5f5760043567fffffffffffffffe8111615a5f576005015460405260206040f35b637128f3b881186132bb5760243610615a5f576004358060a01c615a5f5760405234615a5f576801000000000000000560405160205260005260406000205460605260206060f35b636cce39be81186132e75760043610615a5f5734615a5f57680100000000000000065460405260206040f35b634f02c42081186133135760043610615a5f5734615a5f57680100000000000000075460405260206040f35b63e231bff0811861333f5760043610615a5f5734615a5f57680100000000000000085460405260206040f35b63adfae4ce811861336b5760043610615a5f5734615a5f57680100000000000000095460405260206040f35b63627d2b8381186133975760043610615a5f5734615a5f576801000000000000000a5460405260206040f35b635449b9cb81186133c35760043610615a5f5734615a5f576801000000000000000b5460405260206040f35b505b34156133dc576020615b9160003960005115615a5f575b36615a5f57005b670de0b6b3a764000060405110606052600060805260405160a052606051156134275760a0518015615a5f57806ec097ce7bc90715b34b9f100000000004905060a0525b608060c05260006008905b8060e05260c05160020a61010052670de0b6b3a7640000610100510260a05110613476576101005160a0510460a052608051670de0b6b3a764000060c05102016080525b60c05160011c60c052600101818118613432575050670de0b6b3a764000060e05260006022905b8061010052671bc16d674ec8000060a051106134c75760e0516080510160805260a05160011c60a0525b670de0b6b3a764000060a05160a051020460a05260e05160011c60e05260010181811861349d57505060605161350e576080518060ff1c615a5f5781525061354756613547565b6080518060ff1c615a5f577f80000000000000000000000000000000000000000000000000000000000000008114615a5f576000038152505b565b680100000000000000095463e91f2f4c606052602060606004607c6000855af1613578573d600060003e3d6000fd5b60203d10615a5f576060905051640a3c2abcef818118640a3c2abcef8310021890506040526020615af160003960005163d4387a99606052604051608052602060606024607c6000855af16135d2573d600060003e3d6000fd5b60203d10615a5f576060905051815250565b6135ee60e0613549565b60e05160c052600160a0516020526000526040600020805460e0526001810154610100525060e051613630576000815260c05160208201525061366756613667565b60e05160c051808202811583838304141715615a5f5790509050610100518015615a5f5780820490509050815260c0516020820152505b565b6020615af160003960005163095a0fc6608052602060806004609c845afa613696573d600060003e3d6000fd5b60203d10615a5f576080905051606052600160405160205260005260406000208054608052600181015460a052506080516136d957600081525061370756613707565b608051606051808202811583838304141715615a5f579050905060a0518015615a5f57808204905090508152505b565b6040516080516040516060518015615a5f57808204905090506103e88181186103e88311021890508015615a5f5780683635c9adc5dea00000049050808201828110615a5f5790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a764000003808202811583838304141715615a5f57905090506020615b71600039600051606051808202811583838304141715615a5f57905090508015615a5f578082049050905060a05260a05160c052600160318101905b8060e05260605160e051186137e057613826565b6020615b1160003960005160a0516020615b31600039600051808202811583838304141715615a5f57905090500460a05260a05160c0510160c0526001018181186137cc575b505060c051815250565b6101405161389e576007610180527f4e6f206c6f616e000000000000000000000000000000000000000000000000006101a0526101805061018051806101a001601f826000031636823750506308c379a061014052602061016052601f19601f61018051011660440161015cfd5b6020615af1600039600051638f8654c56101a05260206101a060046101bc845afa6138ce573d600060003e3d6000fd5b60203d10615a5f576101a0905051610180526020615af1600039600051632eb858e76101c052610180516101e05260206101c060246101dc845afa613918573d600060003e3d6000fd5b60203d10615a5f576101c09050516101a052610120516020615ad1600039600051808202811583838304141715615a5f5790509050604052610160516060526801000000000000000b546080526139706101e0613709565b6101e0516101c0526101c0516101a051808202811583838304141715615a5f57905090506101405160018101818110615a5f5790508015615a5f57808204905090506101c0526101c051613a2457600e6101e0527f416d6f756e7420746f6f206c6f77000000000000000000000000000000000000610200526101e0506101e0518061020001601f826000031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b6101c051604052613a366102006133e3565b610200516101e0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101e05113613a9c576101e0516020615b5160003960005160018103818113615a5f5790508082038281136000831218615a5f57905090506101e0525b6101e0516020615b516000396000518015615a5f57808205600160ff1b8314156000198314151715615a5f57905090506101e0526101e051610160518060ff1c615a5f57806104000361040081136000831218615a5f57905080828118828412021890509050610180518082018281126000831218615a5f57905090506101e052610180516101e05113613bf7576020615af160003960005163ec654706610200526101e05160018103818113615a5f579050610220526020610200602461021c845afa613b6f573d600060003e3d6000fd5b60203d10615a5f57610200518060011c615a5f5761024052610240905051613bf757600d610260527f4465627420746f6f2068696768000000000000000000000000000000000000006102805261026050610260518061028001601f826000031636823750506308c379a061022052602061024052601f19601f61026051011660440161023cfd5b6020615af16000396000516386fc88d3610240526020610240600461025c845afa613c27573d600060003e3d6000fd5b60203d10615a5f576102409050516020615af1600039600051632eb858e7610200526101e051610220526020610200602461021c845afa613c6d573d600060003e3d6000fd5b60203d10615a5f5761020090505110613ce657600d610280527f4465627420746f6f2068696768000000000000000000000000000000000000006102a0526102805061028051806102a001601f826000031636823750506308c379a061024052602061026052601f19601f61028051011660440161025cfd5b6101e051815250565b6020615af16000396000516386fc88d3610140526020610140600461015c845afa613d1f573d600060003e3d6000fd5b60203d10615a5f57610140905051610120526020615b516000396000516020615af160003960005163a7db79a5610160526020610160600461017c845afa613d6c573d600060003e3d6000fd5b60203d10615a5f57610160905051670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f579050610120518015615a5f5780820490509050604052613db76101a06133e3565b6101a0510560058101818112615a5f579050610140526020615af1600039600051632eb858e761018052610140516101a0526020610180602461019c845afa613e05573d600060003e3d6000fd5b60203d10615a5f57610180905051610160526020615af160003960005163c16ef2646101a05260206101a060046101bc845afa613e47573d600060003e3d6000fd5b60203d10615a5f576101a0905051610180526000610401905b806101a0526101405160018103818113615a5f57905061014052610180516101405113613e8c57613eee565b610160516101c0526020615b31600039600051610160516020615b11600039600051808202811583838304141715615a5f5790509050046101605261012051610160511115613ee3576101c0518352505050613ef8565b600101818118613e60575b5050610160518152505b565b6020615b91600039600051613f1157606051615a5f575b604051606051808203828111615a5f579050905060805260605115613fce576020615ab160003960005163d0e30db060a052803b15615a5f57600060a0600460bc606051855af1613f67573d600060003e3d6000fd5b506020615ab160003960005163a9059cbb60a0526020615af160003960005160c05260605160e052602060a0604460bc6000855af1613fab573d600060003e3d6000fd5b60203d10615a5f5760a0518060011c615a5f576101005261010090505115615a5f575b60805115614059576020615ab16000396000516323b872dd60a0523360c0526020615af160003960005160e05260805161010052602060a0606460bc6000855af161401e573d600060003e3d6000fd5b3d61403557803b15615a5f5760016101205261404d565b60203d10615a5f5760a0518060011c615a5f57610120525b61012090505115615a5f575b565b608051614069576000614075565b6020615b916000396000515b614102576020615ab16000396000516323b872dd60a0526020615af160003960005160c05260405160e05260605161010052602060a0606460bc6000855af16140c3573d600060003e3d6000fd5b3d6140da57803b15615a5f576001610120526140f2565b60203d10615a5f5760a0518060011c615a5f57610120525b61012090505115615a5f576141d3565b6020615ab16000396000516323b872dd60a0526020615af160003960005160c0523060e05260605161010052602060a0606460bc6000855af161414a573d600060003e3d6000fd5b60203d10615a5f5760a0518060011c615a5f576101205261012090505115615a5f576020615ab1600039600051632e1a7d4d60a05260605160c052803b15615a5f57600060a0602460bc6000855af16141a8573d600060003e3d6000fd5b50600060a05260a0506000600060a05160c0606051604051612710f16141d3573d600060003e3d6000fd5b565b6020615ab160003960005160405114615a5f576060366101c0376020615af1600039600051638f8654c5610220526020610220600461023c845afa61421f573d600060003e3d6000fd5b60203d10615a5f576102209050516101c0526020615af160003960005163ebcb0067610240526101c051610260526020610240602461025c845afa614269573d600060003e3d6000fd5b60203d10615a5f57610240905051610220526020615af16000396000516331f7e306610260526101c051610280526020610260602461027c845afa6142b3573d600060003e3d6000fd5b60203d10615a5f5761026090505161024052600060605181610460015260048101905060a06080516102e05260a0516103005260c0516103205260e051610340528061036052806102e0016000610100518083528060051b60008260058111615a5f57801561433c57905b8060051b61012001518160051b60208801015260010181811861431e575b505082016020019150509050810190506102c0526102c080516020820183610460018281848460045afa505050808301925050508061044052610440505060406106006104405161046060006040515af161439c573d600060003e3d6000fd5b3d604081183d60401002186105e0526105e0805180610260526020820181610280838360045afa5050505061026051602011615a5f57610280516102e05260206102c0526102c06020810151815160200360031b1c90506101e05261026051604011615a5f576102a0516102e05260206102c0526102c06020810151815160200360031b1c9050610200526020615af1600039600051638f8654c56102c05260206102c060046102dc845afa614457573d600060003e3d6000fd5b60203d10615a5f576102c09050516101c05118615a5f576020615af160003960005163ebcb00676102c0526101c0516102e05260206102c060246102dc845afa6144a6573d600060003e3d6000fd5b60203d10615a5f576102c09050516102205118615a5f576020615af16000396000516331f7e3066102c0526101c0516102e05260206102c060246102dc845afa6144f5573d600060003e3d6000fd5b60203d10615a5f576102c09050516102405118615a5f576101c05181526101e051602082015261020051604082015250565b600133602052600052604060002054156145a1576014610360527f4c6f616e20616c726561647920637265617465640000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b600461032051101561461357600f610360527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b603261032051111561468557600f610360527f4e656564206c657373207469636b7300000000000000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b6102e05161012052610300516101405261032051610160526146a8610380613830565b6103805161036052610360516103205160018103818111615a5f5790508060ff1c615a5f578082018281126000831218615a5f5790509050610380526146ef6103c0613549565b6103c0516103a05260013360205260005260406000206103005181556103a0516001820155506801000000000000000a546103c0526103c05160023360205260005260406000205568010000000000000006546103e052336103e05167fffffffffffffffe8111615a5f57600501556103e051680100000000000000053360205260005260406000205560016103e0510168010000000000000006556003546103a051808202811583838304141715615a5f57905090506004548015615a5f578082049050905061030051808201828110615a5f579050905061040052610400516003556103a0516004556020615af160003960005163ab047e006104205233610440526102e051610460526103605161048052610380516104a052803b15615a5f576000610420608461043c6000855af1614830573d600060003e3d6000fd5b50680100000000000000075461030051808201828110615a5f5790509050680100000000000000075561034051156148d5576102e0516040526102c051606052614878613efa565b6020615a9160003960005163a9059cbb61042052336104405261030051610460526020610420604461043c6000855af16148b7573d600060003e3d6000fd5b60203d10615a5f57610420518060011c615a5f576104805261048050505b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26102e051610420526103005161044052610360516104605261038051610480526103c0516104a05260a0610420a2337fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af6102e0516104205261030051610440526040610420a2565b604036610340376103005160a0526149786103806135e4565b61038080516103405260208101516103605250610340516149f9576012610380527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103a0526103805061038051806103a001601f826000031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b610340516102e051808201828110615a5f5790509050610340526020615af160003960005163b461100d6103c052610300516103e05260406103c060246103dc845afa614a4b573d600060003e3d6000fd5b60403d10615a5f576103c0905080516103805260208101516103a052506001610380516103a051030160008112615a5f576103c0526020615af160003960005163f3fef3a3610420526103005161044052670de0b6b3a7640000610460526040610420604461043c6000855af1614ac7573d600060003e3d6000fd5b60403d10615a5f57610420905080516103e052602081015161040052506103e05115614b5357601a610420527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104405261042050610420518061044001601f826000031636823750506308c379a06103e052602061040052601f19601f6104205101166044016103fcfd5b61032051614b7a57610400516102c051808201828110615a5f579050905061040052614b95565b610400516102c051808203828111615a5f5790509050610400525b610400516101205261034051610140526103c05161016052614bb8610440613830565b610440516104205261042051610380516103a051038082018281126000831218615a5f5790509050610440526020615af160003960005163ab047e00610460526103005161048052610400516104a052610420516104c052610440516104e052803b15615a5f576000610460608461047c6000855af1614c3d573d600060003e3d6000fd5b506001610300516020526000526040600020610340518155610360516001820155506801000000000000000a5461046052610460516002610300516020526000526040600020556102e05115614cdd5760035461036051808202811583838304141715615a5f57905090506004548015615a5f57808204905090506102e051808201828110615a5f57905090506104805261048051600355610360516004555b61032051614d2557610300517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af6102c051610480526102e0516104a0526040610480a2614d59565b610300517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb546102c051610480526020610480a25b610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26104005161048052610340516104a052610420516104c052610440516104e052610460516105005260a0610480a2565b680100000000000000065460018103818111615a5f5790506060526801000000000000000560405160205260005260406000205460805260405160805167fffffffffffffffe8111615a5f576005015418615a5f576000680100000000000000056040516020526000526040600020556060516080511015614e795760605167fffffffffffffffe8111615a5f576005015460a05260a05160805167fffffffffffffffe8111615a5f57600501556080516801000000000000000560a0516020526000526040600020555b6060516801000000000000000655565b606051614eed57601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f826000031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b670de0b6b3a764000060c05260a05115614f265760c05160a0518060ff1c615a5f578082038281136000831218615a5f579050905060c0525b6060518060ff1c615a5f576020615af16000396000516362ca4b1860e05260405161010052602060e0602460fc845afa614f65573d600060003e3d6000fd5b60203d10615a5f5760e09050518060ff1c615a5f5760c05180820281191515600160ff1b8414151782158484840514171615615a5f579050905005670de0b6b3a76400008103818113615a5f57905060c052608051156151ac576020615af160003960005163b461100d61012052604051610140526040610120602461013c845afa614ff6573d600060003e3d6000fd5b60403d10615a5f576101209050805160e052602081015161010052506020615af1600039600051638f8654c5610120526020610120600461013c845afa615042573d600060003e3d6000fd5b60203d10615a5f5761012090505160e05113156151ac576020615af16000396000516386fc88d3610180526020610180600461019c845afa615089573d600060003e3d6000fd5b60203d10615a5f57610180905051610160526020615af1600039600051632eb858e76101a05260e0516101c05260206101a060246101bc845afa6150d2573d600060003e3d6000fd5b60203d10615a5f576101a090505161018052610180516101605111156151ac5760c0516060516101605161018051808203828111615a5f57905090506020615af160003960005163544fb5c16101a0526040516101c05260406101a060246101bc845afa615145573d600060003e3d6000fd5b60403d10615a5f576101a0905060208101905051808202811583838304141715615a5f57905090506020615ad1600039600051808202811583838304141715615a5f5790509050048060ff1c615a5f578082018281126000831218615a5f579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff6040511161521957606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b6040366107c0376106405160a05261523a6108006135e4565b61080080516107c05260208101516107e05250610680511561530c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610640516040526107c05160605260016080526106805160a05261529c610800614e89565b61080051131561530c57600f610820527f4e6f7420656e6f7567682072656b7400000000000000000000000000000000006108405261082050610820518061084001601f826000031636823750506308c379a06107e052602061080052601f19601f6108205101166044016107fcfd5b6107c05161080052670de0b6b3a76400006107c0516106a051808202811583838304141715615a5f5790509050046107c0526107c05115615a5f576107c0516108005103610800526106a0516040526106805160605261536d6108406151b4565b61084051610820526020615af160003960005163f3fef3a361088052610640516108a052610820516108c0526040610880604461089c6000855af16153b7573d600060003e3d6000fd5b60403d10615a5f5761088090508051610840526020810151610860525061066051610840511015615448576008610880527f536c6970706167650000000000000000000000000000000000000000000000006108a0526108805061088051806108a001601f826000031636823750506308c379a061084052602061086052601f19601f61088051011660440161085cfd5b610840516107c051808281188284100218905090506108805261088051156154d6576020615a916000396000516323b872dd6108a0526020615af16000396000516108c052306108e052610880516109005260206108a060646108bc6000855af16154b8573d600060003e3d6000fd5b60203d10615a5f576108a0518060011c615a5f576109205261092050505b610840516107c051116155805733604052610860516060526106c0516080526154fd61405b565b6107c0516108405111156158f9576020615a916000396000516323b872dd6108a0526020615af16000396000516108c052336108e0526107c05161084051036109005260206108a060646108bc6000855af161555e573d600060003e3d6000fd5b60203d10615a5f576108a0518060011c615a5f576109205261092050506158f9565b610840516107c051036108a0526106e0516156155733604052610860516060526106c0516080526155af61405b565b6020615a916000396000516323b872dd6108c052336108e05230610900526108a0516109205260206108c060646108dc6000855af16155f3573d600060003e3d6000fd5b60203d10615a5f576108c0518060011c615a5f576109405261094050506158f9565b61086051156156ab576020615ab16000396000516323b872dd6108c0526020615af16000396000516108e0526106e05161090052610860516109205260206108c060646108dc6000855af161566f573d600060003e3d6000fd5b3d61568657803b15615a5f5760016109405261569f565b60203d10615a5f576108c0518060011c615a5f57610940525b61094090505115615a5f575b6106e0516040527f4ea696bb00000000000000000000000000000000000000000000000000000000606052610640516080526108405160a0526108605160c0526107c05160e0526107005180610100528060051b806101208261072060045afa5050506157196109206141d5565b61092080516108c05260208101516108e052604081015161090052506108a0516108e05110156157a9576013610920527f6e6f7420656e6f7567682070726f6365656473000000000000000000000000006109405261092050610920518061094001601f826000031636823750506308c379a06108e052602061090052601f19601f6109205101166044016108fcfd5b6108a0516108e0511115615821576020615a916000396000516323b872dd610920526106e0516109405233610960526108a0516108e05103610980526020610920606461093c6000855af1615803573d600060003e3d6000fd5b60203d10615a5f57610920518060011c615a5f576109a0526109a050505b6020615a916000396000516323b872dd610920526106e0516109405230610960526108a051610980526020610920606461093c6000855af1615868573d600060003e3d6000fd5b60203d10615a5f57610920518060011c615a5f576109a0526109a0505061090051156158f9576020615ab16000396000516323b872dd610920526106e05161094052336109605261090051610980526020610920606461093c6000855af16158d5573d600060003e3d6000fd5b60203d10615a5f57610920518060011c615a5f576109a0526109a090505115615a5f575b68010000000000000008546107c051808201828110615a5f5790509050680100000000000000085560016106405160205260005260406000206108005181556107e051600182015550610640517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d0610860516108a0526107c0516108c05260406108a0a261064051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d0610860516108a052610840516108c0526107c0516108e05260606108a0a361080051615a0a57610640517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366108a03760a06108a0a261064051604052615a0a614dae565b6003546107e051808202811583838304141715615a5f57905090506004548015615a5f57808204905090506108a0526107c0516108a0516107c05180828118828411021890509050036003556107e051600455565b600080fda165767970657283000307000b5b670de0b6b3a764000060405110606052600060805260405160a05260605115615ecc5760a0518015615fee57806ec097ce7bc90715b34b9f100000000004905060a0525b608060c05260006008905b8060e05260c05160020a61010052670de0b6b3a7640000610100510260a05110615f1b576101005160a0510460a052608051670de0b6b3a764000060c05102016080525b60c05160011c60c052600101818118615ed7575050670de0b6b3a764000060e05260006022905b8061010052671bc16d674ec8000060a05110615f6c5760e0516080510160805260a05160011c60a0525b670de0b6b3a764000060a05160a051020460a05260e05160011c60e052600101818118615f42575050606051615fb3576080518060ff1c615fee57815250615fec56615fec565b6080518060ff1c615fee577f80000000000000000000000000000000000000000000000000000000000000008114615fee576000038152505b565b600080fd0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca00000000000000000000000001e7d3bf98d3f8d8ce193236c3e0ec4b00e32daae000000000000000000000000000000000000000000000000013fbe85edc9000000000000000000000000000000000000000000000000000000d529ae9e86000000000000000000000000000037417b2238aa52d0dd2d6252d989e728e8f706e4

Deployed Bytecode

0x6003361161000c576133c5565b60003560e01c6323cfed0381186100565760643610615a5f57600054600214615a5f576002600055346102c052606060046102e03760016103405261004f614527565b6003600055005b63bc61ea2381186102575760c43610615a5f576064358060a01c615a5f57610640526084356004016005813511615a5f5780358061066052602082018160051b80826106803750505050600054600214615a5f5760026000556020615a9160003960005163a9059cbb610720526106405161074052602435610760526020610720604461073c6000855af16100f0573d600060003e3d6000fd5b60203d10615a5f57610720518060011c615a5f57610780526107805050610640516040527fe62214fe0000000000000000000000000000000000000000000000000000000060605233608052600060a0526040600460c0376106605180610100528060051b806101208261068060045afa50505061016f6107406141d5565b61074060408101905051610720526004356040523460605261018f613efa565b6020615ab16000396000516323b872dd6107405261064051610760526020615af160003960005161078052610720516107a0526020610740606461075c6000855af16101e0573d600060003e3d6000fd5b3d6101f757803b15615a5f5760016107c052610210565b60203d10615a5f57610740518060011c615a5f576107c0525b6107c090505115615a5f5760006102c05260043561072051808201828110615a5f57905090506102e0526040602461030037600061034052610250614527565b6003600055005b636f972f1281186102745760243610615a5f573361052052610297565b6324049e5781186102ee5760443610615a5f576024358060a01c615a5f57610520525b600054600214615a5f5760026000556004356102b2576102e7565b6004356102c05260006102e05261052051610300526000610320526102d561495f565b600435604052346060526102e7613efa565b6003600055005b63dd171e7c81186103db5760443610615a5f57600054600214615a5f57600260005560243561031c576103d4565b604060046102c037336103005260006103205261033761495f565b600435156103515760043560405234606052610351613efa565b6020615a9160003960005163a9059cbb610520523361054052602435610560526020610520604461053c6000855af161038f573d600060003e3d6000fd5b60203d10615a5f57610520518060011c615a5f576105805261058050506801000000000000000754602435808201828110615a5f579050905068010000000000000007555b6003600055005b63371fd8e681186104235760243610615a5f57336102c0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102e0526001610300526104e2565b63acb7081581186104755760443610615a5f576024358060a01c615a5f576102c0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102e0526001610300526104e2565b63b4440df481186104a95760643610615a5f576024358060a01c615a5f576102c0526044356102e0526001610300526104e2565b6337671f938118610be25760843610615a5f576024358060a01c615a5f576102c0526044356102e0526064358060011c615a5f57610300525b600054600214615a5f5760026000556004356104fd57610bdb565b604036610320376102c05160a0526105166103606135e4565b6103608051610320526020810151610340525061032051610597576012610360527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b6103205160043580828118828410021890509050610360526103605161032051036103205261032051610750576020615af160003960005163f3fef3a36103c0526102c0516103e052670de0b6b3a76400006104005260406103c060446103dc6000855af161060b573d600060003e3d6000fd5b60403d10615a5f576103c0905080516103805260208101516103a0525061038051156106aa57336102c05118615a5f576020615a916000396000516323b872dd6103c0526020615af16000396000516103e0526102c05161040052610380516104205260206103c060646103dc6000855af161068c573d600060003e3d6000fd5b60203d10615a5f576103c0518060011c615a5f576104405261044050505b6103a051156106d0576102c0516040526103a051606052610300516080526106d061405b565b6102c0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366103c03760a06103c0a26102c0517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d06103a0516103c052610360516103e05260406103c0a26102c051604052610add614dae56610add565b6020615af160003960005163c16ef2646103a05260206103a060046103bc845afa610780573d600060003e3d6000fd5b60203d10615a5f576103a0905051610380526102e0516103805113615a5f576020615af160003960005163b461100d6103e0526102c0516104005260406103e060246103fc845afa6107d7573d600060003e3d6000fd5b60403d10615a5f576103e0905080516103a05260208101516103c0525060016103a0516103c051030160008112615a5f576103e052600061040052610380516103a051136108e35760026102c051602052600052604060002054610400526102c0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6104205261032051610440526103a051610460526103c05161048052610400516104a05260a0610420a26102c0517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d060006104205261036051610440526040610420a2610aa2565b6020615af160003960005163f3fef3a3610460526102c05161048052670de0b6b3a76400006104a0526040610460604461047c6000855af161092a573d600060003e3d6000fd5b60403d10615a5f57610460905080516104205260208101516104405250610440516101205261032051610140526103e0516101605261096a610480613830565b6104805161046052610460516103a0516103c051038082018281126000831218615a5f5790509050610480526020615af160003960005163ab047e006104a0526102c0516104c052610440516104e05261046051610500526104805161052052803b15615a5f5760006104a060846104bc6000855af16109ef573d600060003e3d6000fd5b506801000000000000000a54610400526104005160026102c0516020526000526040600020556102c0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610440516104a052610320516104c052610460516104e0526104805161050052610400516105205260a06104a0a26102c0517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d060006104a052610360516104c05260406104a0a25b336102c05114610add5760016102c0516040526103205160605260006080526104005160a052610ad3610420614e89565b6104205112615a5f575b6020615a916000396000516323b872dd61038052336103a052306103c052610360516103e0526020610380606461039c6000855af1610b21573d600060003e3d6000fd5b60203d10615a5f57610380518060011c615a5f57610400526104005050680100000000000000085461036051808201828110615a5f5790509050680100000000000000085560016102c05160205260005260406000206103205181556103405160018201555060035461034051808202811583838304141715615a5f57905090506004548015615a5f5780820490509050610380526103605161038051610360518082811882841102189050905003600355610340516004555b6003600055005b63c45a01558118610c0e5760043610615a5f5734615a5f576020615a7160003960005160405260206040f35b632a9439458118610c3a5760043610615a5f5734615a5f576020615af160003960005160405260206040f35b632621db2f8118610c665760043610615a5f5734615a5f576020615ab160003960005160405260206040f35b639b6c56ec8118610cac5760243610615a5f576004358060a01c615a5f5760c05234615a5f57600054600214615a5f57602060c051604052610ca860e0613669565b60e0f35b63a21adb9e8118610cf85760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f576001604051602052600052604060002054151560605260206060f35b6331dc3ca88118610d8c5760043610615a5f5734615a5f576020615af160003960005163095a0fc6606052602060606004607c845afa610d3d573d600060003e3d6000fd5b60203d10615a5f576060905051604052600354606052600454608052606051604051808202811583838304141715615a5f57905090506080518015615a5f578082049050905060a052602060a0f35b639a4971968118610ece5760443610615a5f5734615a5f57600054600214615a5f576004356020615ad1600039600051808202811583838304141715615a5f57905090506040526024356060526801000000000000000b54608052610df2610200613709565b610200516101e0526001670de0b6b3a76400006101e051610e14610220613cef565b61022051808202811583838304141715615a5f579050905004600181811860018311021890500361020052670de0b6b3a764000061020051670de05bc096e9c000810281670de05bc096e9c000820418615a5f5790500461020052610200516020615a916000396000516370a082316102205230610240526020610220602461023c845afa610ea8573d600060003e3d6000fd5b60203d10615a5f5761022090505180828118828410021890509050610260526020610260f35b63a75732068118610ffc5760443610615a5f5734615a5f57600054600214615a5f57670de05bc096e9c0006020615ad1600039600051600435670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f579050610f306101e0613cef565b6101e0518015615a5f5780820490509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f579050670de0b6b3a76400006040526024356060526801000000000000000b54608052610f8a610200613709565b610200518015615a5f57808204905090506024356024356107d08101818110615a5f579050808202811583838304141715615a5f5790509050808201828110615a5f579050905004670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f57905004610220526020610220f35b63720fb25481186110385760643610615a5f5734615a5f57600054600214615a5f57602060606004610120376110336102c0613830565b6102c0f35b63d14ff5b681186110565760243610615a5f57600161052052611079565b632e4af52a81186110d95760443610615a5f576024358060011c615a5f57610520525b34615a5f57600054600214615a5f576002600055600435611099576110d2565b6004356102c05260006102e05233610300526001610320526110b961495f565b33604052600435606052610520516080526110d261405b565b6003600055005b63152f65cb81186118a65760643610615a5f576004358060a01c615a5f57610640526024356004016005813511615a5f5780358061066052602082018160051b8082610680375050505034615a5f57600054600214615a5f5760026000556020615af160003960005163b461100d6107605233610780526040610760602461077c845afa61116c573d600060003e3d6000fd5b60403d10615a5f576107609050805161072052602081015161074052506020615af160003960005163f3fef3a36107a052336107c052670de0b6b3a76400006107e05260406107a060446107bc6000855af16111cd573d600060003e3d6000fd5b60403d10615a5f576107a09050805161076052602081015161078052506040366107a0373360a0526112006107e06135e4565b6107e080516107a05260208101516107c052506020615ab16000396000516323b872dd6107e0526020615af1600039600051610800526106405161082052610780516108405260206107e060646107fc6000855af1611264573d600060003e3d6000fd5b3d61127b57803b15615a5f57600161086052611294565b60203d10615a5f576107e0518060011c615a5f57610860525b6108605050610640516040527fef67dc7400000000000000000000000000000000000000000000000000000000606052336080526107605160a0526107805160c0526107a05160e0526106605180610100528060051b806101208261068060045afa5050506113046108406141d5565b61084080516107e052602081015161080052604081015161082052506108005161076051808201828110615a5f5790509050610840526108405115615a5f576000610860526107a0516108405110156115a55760016107205161074051030160008112615a5f57610880526107e051610720511315615a5f576108005161086052610800516107a051036107a05261082051610120526107a0516101405261088051610160526113b56108c0613830565b6108c0516108a0526108a0516107205161074051038082018281126000831218615a5f57905090506108c0526020615af160003960005163ab047e006108e052336109005261082051610920526108a051610940526108c05161096052803b15615a5f5760006108e060846108fc6000855af1611437573d600060003e3d6000fd5b506801000000000000000a546108e0526108e0516002336020526000526040600020556020615ab16000396000516323b872dd6109005261064051610920526020615af16000396000516109405261082051610960526020610900606461091c6000855af16114ab573d600060003e3d6000fd5b3d6114c257803b15615a5f576001610980526114db565b60203d10615a5f57610900518060011c615a5f57610980525b61098090505115615a5f576020615a916000396000516323b872dd610900526106405161092052306109405261080051610960526020610900606461091c6000855af161152d573d600060003e3d6000fd5b60203d10615a5f57610900518060011c615a5f57610980526109805050337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261082051610900526107a051610920526108a051610940526108c051610960526108e0516109805260a0610900a26000610780526117ce565b6107a0516108605260006107a052336040526115bf614dae565b610800511561162d576020615a916000396000516323b872dd61088052610640516108a052306108c052610800516108e0526020610880606461089c6000855af161160f573d600060003e3d6000fd5b60203d10615a5f57610880518060011c615a5f576109005261090050505b61076051156116a2576020615a916000396000516323b872dd610880526020615af16000396000516108a052306108c052610760516108e0526020610880606461089c6000855af1611684573d600060003e3d6000fd5b60203d10615a5f57610880518060011c615a5f576109005261090050505b61086051610840511115611712576020615a9160003960005163a9059cbb61088052336108a0526108605161084051036108c0526020610880604461089c6000855af16116f4573d600060003e3d6000fd5b60203d10615a5f57610880518060011c615a5f576108e0526108e050505b610820511561179e576020615ab16000396000516323b872dd61088052610640516108a052336108c052610820516108e0526020610880606461089c6000855af1611762573d600060003e3d6000fd5b3d61177957803b15615a5f57600161090052611792565b60203d10615a5f57610880518060011c615a5f57610900525b61090090505115615a5f575b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366108803760a0610880a25b337f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d06107805161088052610860516108a0526040610880a2680100000000000000085461086051808201828110615a5f5790509050680100000000000000085560013360205260005260406000206107a05181556107c0516001820155506003546107c051808202811583838304141715615a5f57905090506004548015615a5f57808204905090506108805261086051610880516108605180828118828411021890509050036003556107c0516004556003600055005b630b8db68181186118c45760843610615a5f576000610300526118df565b6322c714538118611e865760a43610615a5f57608435610300525b6004358060a01c615a5f576102c0526064358060011c615a5f576102e05234615a5f57600054600214615a5f576020615af160003960005163544fb5c1610360526102c051610380526040610360602461037c845afa611944573d600060003e3d6000fd5b60403d10615a5f57610360905080516103205260208101516103405250610340516020615ad1600039600051808202811583838304141715615a5f5790509050610340526020615af160003960005163b461100d6103a0526102c0516103c05260406103a060246103bc845afa6119c0573d600060003e3d6000fd5b60403d10615a5f576103a09050805161036052602081015161038052506102c0516040526119ef6103c0613669565b6103c0518060ff1c615a5f576103a052610300516103c05260006103e0526103a05115611a515760026102c0516020526000526040600020548060ff1c615a5f576103e05260016103605161038051030160008112615a5f576103c052611a8e565b6801000000000000000a548060ff1c615a5f576103e0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610360525b606036610400376103a0516044358082018281126000831218615a5f57905090506103a05260016103a0511215611b25576011610460527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104805261046050610460518061048001601f826000031636823750506308c379a061042052602061044052601f19601f61046051011660440161043cfd5b6020615af160003960005163c16ef264610480526020610480600461049c845afa611b55573d600060003e3d6000fd5b60203d10615a5f5761048090505161046052610460516103605113611b7b576000611b90565b60243515611b8a576001611b90565b60443515155b611c115761036051610400526020615af16000396000516362ca4b18610480526102c0516104a0526020610480602461049c845afa611bd4573d600060003e3d6000fd5b60203d10615a5f57610480905051670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f5790508060ff1c615a5f5761044052611c73565b610340518060ff1c615a5f576024358082018281126000831218615a5f5790509050610420526104205160008112615a5f57610120526103a05160008112615a5f57610140526103c05161016052611c6a610480613830565b61048051610400525b6020615af1600039600051632eb858e76104a052610400516104c05260206104a060246104bc845afa611cab573d600060003e3d6000fd5b60203d10615a5f576104a09050518060ff1c615a5f576104805261046051610360511315611d30576104205160008112615a5f576040526103c0516060526000608052611cf96104a0613709565b6104a0518060ff1c615a5f576104805180820281191515600160ff1b8414151782158484840514171615615a5f5790509050610440525b6103a05161044051056104a0526104a051670de0b6b3a76400006104a0516103e05180820281191515600160ff1b8414151782158484840514171615615a5f5790509050058082038281136000831218615a5f5790509050670de0b6b3a76400008103818113615a5f5790506104a0526102e05115611e7f5761046051610400511315611e7f57610480516020615af16000396000516386fc88d36104e05260206104e060046104fc845afa611deb573d600060003e3d6000fd5b60203d10615a5f576104e09050518060ff1c615a5f5780828118828413021890509050610480518082038281136000831218615a5f57905090506104c05260016104c05112611e7f576104a0516103a0516104c0516104205180820281191515600160ff1b8414151782158484840514171615615a5f5790509050058082018281126000831218615a5f57905090506104a0525b60206104a0f35b63bcbaf4878118611ea45760443610615a5f5760016109e052611ec7565b633ecdb8288118611f535760643610615a5f576044358060011c615a5f576109e0525b6004358060a01c615a5f576109c05234615a5f57600054600214615a5f5760026000556000610a0052336109c05114611f115760026109c051602052600052604060002054610a00525b6109c0516106405260243561066052610a005161068052670de0b6b3a76400006106a0526109e0516106c0526040366106e037611f4c615221565b6003600055005b63036aed88811861206a5760e43610615a5f576004358060a01c615a5f576109c0526064358060011c615a5f576109e0526084358060a01c615a5f57610a005260a4356004016005813511615a5f57803580610a2052602082018160051b8082610a40375050505034615a5f57600054600214615a5f5760026000556000610ae052336109c05114611ff65760026109c051602052600052604060002054610ae0525b6109c0516106405260243561066052610ae05161068052604435670de0b6b3a7640000818118670de0b6b3a76400008310021890506106a0526109e0516106c052610a00516106e052610a205180610700528060051b8061072082610a4060045afa505050612063615221565b6003600055005b631b25cdaf811861208e5760243610615a5f57670de0b6b3a764000060e0526120a8565b63546e040d81186121d45760443610615a5f5760243560e0525b6004358060a01c615a5f5760c05234615a5f57600054600214615a5f576000610100523360c051146120ea57600260c051602052600052604060002054610100525b60e051604052610100516060526121026101406151b4565b6101405161012052670de0b6b3a76400006020615af160003960005163544fb5c16101605260c051610180526040610160602461017c845afa61214a573d600060003e3d6000fd5b60403d10615a5f5761016090505161012051808202811583838304141715615a5f57905090500461014052670de0b6b3a764000060c05160405261218f610180613669565b6101805160e051808202811583838304141715615a5f579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f35b63e2d8ebee81186121f25760243610615a5f57600061022052612215565b638908ea8281186122a15760443610615a5f576024358060011c615a5f57610220525b6004358060a01c615a5f576102005234615a5f57600054600214615a5f57602061020051610280526102005160405261224f610240613669565b610240516102a052610220516102c0526002610200516020526000526040600020546102e052610280516040526102a0516060526102c0516080526102e05160a05261229c610260614e89565b610260f35b627c98ab81186122bf5760043610615a5f5760403661020037612300565b6380e8f6ec81186122e45760243610615a5f5760043561020052600061022052612300565b6390f8667d81186125815760443610615a5f5760406004610200375b34615a5f57600054600214615a5f576801000000000000000654610240526102205161026052610220516123375761024051610260525b610200516102805260006102a0526000620f4240905b80620273c052610240516102805110156123715761026051620273c0511815612374565b60015b1561237e576124f0565b6102805167fffffffffffffffe8111615a5f5760050154620273e052620273e0516040526123ae62027420613669565b62027420516202740052620273e051604052620274005160605260016080526002620273e05160205260005260406000205460a0526123ef62027440614e89565b620274405162027420527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6202742051136124d0576020615af160003960005163544fb5c16202748052620273e051620274a05260406202748060246202749c845afa612461573d600060003e3d6000fd5b60403d10615a5f576202748090508051620274405260208101516202746052506102a0516103e78111615a5f57600181016102a05260a081026102c001620273e05181526202744051602082015262027460516040820152620274005160608201526202742051608082015250505b6102805160018101818110615a5f5790506102805260010181811861234d575b5050602080620273c05280620273c00160006102a05180835260a081026000826103e88111615a5f57801561256b57905b60a08102602087010160a082026102c00180518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050600101818118612521575b50508201602001915050905081019050620273c0f35b63d9f11a6481186125d65760043610615a5f5734615a5f5760206020615af160003960005163f2388acb604052602060406004605c845afa6125c8573d600060003e3d6000fd5b60203d10615a5f5760409050f35b632c5089c381186127365760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f576020615af160003960005163e8dd1ef1606052604051608052602060606024607c845afa612639573d600060003e3d6000fd5b60203d10615a5f576060518060011c615a5f5760a05260a090505115615a5f576020615af160003960005163b461100d60a05260405160c052604060a0602460bc845afa61268c573d600060003e3d6000fd5b60403d10615a5f5760a0905080516060526020810151608052506020615af1600039600051632eb858e760a05260605160c052602060a0602460bc845afa6126d9573d600060003e3d6000fd5b60203d10615a5f5760a0905051610120526020615af16000396000516324299b7a60e05260805161010052602060e0602460fc845afa61271e573d600060003e3d6000fd5b60203d10615a5f5760e0905051610140526040610120f35b63ec74d0a881186128545760243610615a5f576004358060a01c615a5f5760c05234615a5f57600054600214615a5f576020615af160003960005163544fb5c16101205260c051610140526040610120602461013c845afa61279d573d600060003e3d6000fd5b60403d10615a5f576101209050805160e052602081015161010052506020615af160003960005163b461100d6101605260c051610180526040610160602461017c845afa6127f0573d600060003e3d6000fd5b60403d10615a5f57610160905080516101205260208101516101405250610100516101805260e0516101a05260c05160405261282d610160613669565b610160516101c05260016101205161014051030160008112615a5f576101e0526080610180f35b634189617d811861297d5760243610615a5f5734615a5f576020615a7160003960005163f851a440604052602060406004605c845afa612899573d600060003e3d6000fd5b60203d10615a5f576040518060a01c615a5f5760805260809050513318615a5f5767016345785d8a000060043511156128d35760006128dd565b620f424060043510155b61293e5760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6020615af1600039600051631aa02d59604052600435606052803b15615a5f57600060406024605c6000855af161297a573d600060003e3d6000fd5b50005b63a5b4804a8118612a915760243610615a5f5734615a5f576020615a7160003960005163f851a440604052602060406004605c845afa6129c2573d600060003e3d6000fd5b60203d10615a5f576040518060a01c615a5f5760805260809050513318615a5f57670de0b6b3a76400006004351115612a525760086040527f486967682066656500000000000000000000000000000000000000000000000060605260405060405180606001601f826000031636823750506308c379a06000526020602052601f19601f6040510116604401601cfd5b6020615af1600039600051633217902f604052600435606052803b15615a5f57600060406024605c6000855af1612a8e573d600060003e3d6000fd5b50005b6381d2f1b78118612b885760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f5760026000556020615a7160003960005163f851a440606052602060606004607c845afa612af3573d600060003e3d6000fd5b60203d10615a5f576060518060a01c615a5f5760a05260a09050513318615a5f57604051680100000000000000095560405163e91f2f4c606052602060606004607c6000855af1612b49573d600060003e3d6000fd5b60203d10615a5f57606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a16003600055005b632a0c35868118612c7a5760443610615a5f5734615a5f57600054600214615a5f5760026000556020615a7160003960005163f851a440604052602060406004605c845afa612bdc573d600060003e3d6000fd5b60203d10615a5f576040518060a01c615a5f5760805260809050513318615a5f576024356004351115615a5f57662386f26fc1000060243510615a5f576706f05b59d3b2000060043511615a5f576024356801000000000000000a556004356801000000000000000b557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a16003600055005b63cc1891c78118612d415760243610615a5f576004358060a01c615a5f5760405234615a5f57600054600214615a5f5760026000556020615a7160003960005163f851a440606052602060606004607c845afa612cdc573d600060003e3d6000fd5b60203d10615a5f576060518060a01c615a5f5760a05260a09050513318615a5f576020615af160003960005163cc1891c7606052604051608052803b15615a5f57600060606024607c6000855af1612d39573d600060003e3d6000fd5b506003600055005b631b1800e38118612e1c5760043610615a5f5734615a5f576020615af160003960005163095a0fc6606052602060606004607c845afa612d86573d600060003e3d6000fd5b60203d10615a5f576060905051604052600354606052600454608052606051604051808202811583838304141715615a5f57905090506080518015615a5f57808204905090506060526060516801000000000000000854808201828110615a5f5790509050606052680100000000000000075460a05260a05160605160a051808281188284110218905090500360c052602060c0f35b631e0cfcef81186131fc5760043610615a5f5734615a5f57600054600214615a5f5760026000556020615a7160003960005163cab4d3db60c052602060c0600460dc845afa612e70573d600060003e3d6000fd5b60203d10615a5f5760c0518060a01c615a5f576101005261010090505160a0526020615af160003960005163d1fea73360e052602060e0600460fc845afa612ebd573d600060003e3d6000fd5b60203d10615a5f5760e090505160c0526020615af16000396000516389960ba7610100526020610100600461011c845afa612efd573d600060003e3d6000fd5b60203d10615a5f5761010090505160e05260c05115612f83576020615a916000396000516323b872dd610100526020615af16000396000516101205260a0516101405260c051610160526020610100606461011c6000855af1612f65573d600060003e3d6000fd5b60203d10615a5f57610100518060011c615a5f576101805261018050505b60e05115613016576020615ab16000396000516323b872dd610100526020615af16000396000516101205260a0516101405260e051610160526020610100606461011c6000855af1612fda573d600060003e3d6000fd5b3d612ff157803b15615a5f5760016101805261300a565b60203d10615a5f57610100518060011c615a5f57610180525b61018090505115615a5f575b6020615af160003960005163822fe50761010052803b15615a5f576000610100600461011c6000855af161304f573d600060003e3d6000fd5b5061305b610120613549565b610120516101005260035461012052600454610140526101205161010051808202811583838304141715615a5f5790509050610140518015615a5f57808204905090506101205261010051610140526101205160035561014051600455610120516801000000000000000854808201828110615a5f57905090506101605268010000000000000007546101805261018051610160511161313e577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f60006101a052610120516101c05260406101a0a160006101a05260206101a06131f5566131f5565b610160516801000000000000000755610180516101605103610160526020615a9160003960005163a9059cbb6101a05260a0516101c052610160516101e05260206101a060446101bc6000855af161319b573d600060003e3d6000fd5b60203d10615a5f576101a0518060011c615a5f576102005261020050507f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f610160516101a052610120516101c05260406101a0a160206101605b6003600055f35b635457ff7b811861323c5760243610615a5f576004358060a01c615a5f5760405234615a5f57600260405160205260005260406000205460605260206060f35b63e1ec3c6881186132735760243610615a5f5734615a5f5760043567fffffffffffffffe8111615a5f576005015460405260206040f35b637128f3b881186132bb5760243610615a5f576004358060a01c615a5f5760405234615a5f576801000000000000000560405160205260005260406000205460605260206060f35b636cce39be81186132e75760043610615a5f5734615a5f57680100000000000000065460405260206040f35b634f02c42081186133135760043610615a5f5734615a5f57680100000000000000075460405260206040f35b63e231bff0811861333f5760043610615a5f5734615a5f57680100000000000000085460405260206040f35b63adfae4ce811861336b5760043610615a5f5734615a5f57680100000000000000095460405260206040f35b63627d2b8381186133975760043610615a5f5734615a5f576801000000000000000a5460405260206040f35b635449b9cb81186133c35760043610615a5f5734615a5f576801000000000000000b5460405260206040f35b505b34156133dc576020615b9160003960005115615a5f575b36615a5f57005b670de0b6b3a764000060405110606052600060805260405160a052606051156134275760a0518015615a5f57806ec097ce7bc90715b34b9f100000000004905060a0525b608060c05260006008905b8060e05260c05160020a61010052670de0b6b3a7640000610100510260a05110613476576101005160a0510460a052608051670de0b6b3a764000060c05102016080525b60c05160011c60c052600101818118613432575050670de0b6b3a764000060e05260006022905b8061010052671bc16d674ec8000060a051106134c75760e0516080510160805260a05160011c60a0525b670de0b6b3a764000060a05160a051020460a05260e05160011c60e05260010181811861349d57505060605161350e576080518060ff1c615a5f5781525061354756613547565b6080518060ff1c615a5f577f80000000000000000000000000000000000000000000000000000000000000008114615a5f576000038152505b565b680100000000000000095463e91f2f4c606052602060606004607c6000855af1613578573d600060003e3d6000fd5b60203d10615a5f576060905051640a3c2abcef818118640a3c2abcef8310021890506040526020615af160003960005163d4387a99606052604051608052602060606024607c6000855af16135d2573d600060003e3d6000fd5b60203d10615a5f576060905051815250565b6135ee60e0613549565b60e05160c052600160a0516020526000526040600020805460e0526001810154610100525060e051613630576000815260c05160208201525061366756613667565b60e05160c051808202811583838304141715615a5f5790509050610100518015615a5f5780820490509050815260c0516020820152505b565b6020615af160003960005163095a0fc6608052602060806004609c845afa613696573d600060003e3d6000fd5b60203d10615a5f576080905051606052600160405160205260005260406000208054608052600181015460a052506080516136d957600081525061370756613707565b608051606051808202811583838304141715615a5f579050905060a0518015615a5f57808204905090508152505b565b6040516080516040516060518015615a5f57808204905090506103e88181186103e88311021890508015615a5f5780683635c9adc5dea00000049050808201828110615a5f5790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a764000003808202811583838304141715615a5f57905090506020615b71600039600051606051808202811583838304141715615a5f57905090508015615a5f578082049050905060a05260a05160c052600160318101905b8060e05260605160e051186137e057613826565b6020615b1160003960005160a0516020615b31600039600051808202811583838304141715615a5f57905090500460a05260a05160c0510160c0526001018181186137cc575b505060c051815250565b6101405161389e576007610180527f4e6f206c6f616e000000000000000000000000000000000000000000000000006101a0526101805061018051806101a001601f826000031636823750506308c379a061014052602061016052601f19601f61018051011660440161015cfd5b6020615af1600039600051638f8654c56101a05260206101a060046101bc845afa6138ce573d600060003e3d6000fd5b60203d10615a5f576101a0905051610180526020615af1600039600051632eb858e76101c052610180516101e05260206101c060246101dc845afa613918573d600060003e3d6000fd5b60203d10615a5f576101c09050516101a052610120516020615ad1600039600051808202811583838304141715615a5f5790509050604052610160516060526801000000000000000b546080526139706101e0613709565b6101e0516101c0526101c0516101a051808202811583838304141715615a5f57905090506101405160018101818110615a5f5790508015615a5f57808204905090506101c0526101c051613a2457600e6101e0527f416d6f756e7420746f6f206c6f77000000000000000000000000000000000000610200526101e0506101e0518061020001601f826000031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b6101c051604052613a366102006133e3565b610200516101e0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101e05113613a9c576101e0516020615b5160003960005160018103818113615a5f5790508082038281136000831218615a5f57905090506101e0525b6101e0516020615b516000396000518015615a5f57808205600160ff1b8314156000198314151715615a5f57905090506101e0526101e051610160518060ff1c615a5f57806104000361040081136000831218615a5f57905080828118828412021890509050610180518082018281126000831218615a5f57905090506101e052610180516101e05113613bf7576020615af160003960005163ec654706610200526101e05160018103818113615a5f579050610220526020610200602461021c845afa613b6f573d600060003e3d6000fd5b60203d10615a5f57610200518060011c615a5f5761024052610240905051613bf757600d610260527f4465627420746f6f2068696768000000000000000000000000000000000000006102805261026050610260518061028001601f826000031636823750506308c379a061022052602061024052601f19601f61026051011660440161023cfd5b6020615af16000396000516386fc88d3610240526020610240600461025c845afa613c27573d600060003e3d6000fd5b60203d10615a5f576102409050516020615af1600039600051632eb858e7610200526101e051610220526020610200602461021c845afa613c6d573d600060003e3d6000fd5b60203d10615a5f5761020090505110613ce657600d610280527f4465627420746f6f2068696768000000000000000000000000000000000000006102a0526102805061028051806102a001601f826000031636823750506308c379a061024052602061026052601f19601f61028051011660440161025cfd5b6101e051815250565b6020615af16000396000516386fc88d3610140526020610140600461015c845afa613d1f573d600060003e3d6000fd5b60203d10615a5f57610140905051610120526020615b516000396000516020615af160003960005163a7db79a5610160526020610160600461017c845afa613d6c573d600060003e3d6000fd5b60203d10615a5f57610160905051670de0b6b3a7640000810281670de0b6b3a7640000820418615a5f579050610120518015615a5f5780820490509050604052613db76101a06133e3565b6101a0510560058101818112615a5f579050610140526020615af1600039600051632eb858e761018052610140516101a0526020610180602461019c845afa613e05573d600060003e3d6000fd5b60203d10615a5f57610180905051610160526020615af160003960005163c16ef2646101a05260206101a060046101bc845afa613e47573d600060003e3d6000fd5b60203d10615a5f576101a0905051610180526000610401905b806101a0526101405160018103818113615a5f57905061014052610180516101405113613e8c57613eee565b610160516101c0526020615b31600039600051610160516020615b11600039600051808202811583838304141715615a5f5790509050046101605261012051610160511115613ee3576101c0518352505050613ef8565b600101818118613e60575b5050610160518152505b565b6020615b91600039600051613f1157606051615a5f575b604051606051808203828111615a5f579050905060805260605115613fce576020615ab160003960005163d0e30db060a052803b15615a5f57600060a0600460bc606051855af1613f67573d600060003e3d6000fd5b506020615ab160003960005163a9059cbb60a0526020615af160003960005160c05260605160e052602060a0604460bc6000855af1613fab573d600060003e3d6000fd5b60203d10615a5f5760a0518060011c615a5f576101005261010090505115615a5f575b60805115614059576020615ab16000396000516323b872dd60a0523360c0526020615af160003960005160e05260805161010052602060a0606460bc6000855af161401e573d600060003e3d6000fd5b3d61403557803b15615a5f5760016101205261404d565b60203d10615a5f5760a0518060011c615a5f57610120525b61012090505115615a5f575b565b608051614069576000614075565b6020615b916000396000515b614102576020615ab16000396000516323b872dd60a0526020615af160003960005160c05260405160e05260605161010052602060a0606460bc6000855af16140c3573d600060003e3d6000fd5b3d6140da57803b15615a5f576001610120526140f2565b60203d10615a5f5760a0518060011c615a5f57610120525b61012090505115615a5f576141d3565b6020615ab16000396000516323b872dd60a0526020615af160003960005160c0523060e05260605161010052602060a0606460bc6000855af161414a573d600060003e3d6000fd5b60203d10615a5f5760a0518060011c615a5f576101205261012090505115615a5f576020615ab1600039600051632e1a7d4d60a05260605160c052803b15615a5f57600060a0602460bc6000855af16141a8573d600060003e3d6000fd5b50600060a05260a0506000600060a05160c0606051604051612710f16141d3573d600060003e3d6000fd5b565b6020615ab160003960005160405114615a5f576060366101c0376020615af1600039600051638f8654c5610220526020610220600461023c845afa61421f573d600060003e3d6000fd5b60203d10615a5f576102209050516101c0526020615af160003960005163ebcb0067610240526101c051610260526020610240602461025c845afa614269573d600060003e3d6000fd5b60203d10615a5f57610240905051610220526020615af16000396000516331f7e306610260526101c051610280526020610260602461027c845afa6142b3573d600060003e3d6000fd5b60203d10615a5f5761026090505161024052600060605181610460015260048101905060a06080516102e05260a0516103005260c0516103205260e051610340528061036052806102e0016000610100518083528060051b60008260058111615a5f57801561433c57905b8060051b61012001518160051b60208801015260010181811861431e575b505082016020019150509050810190506102c0526102c080516020820183610460018281848460045afa505050808301925050508061044052610440505060406106006104405161046060006040515af161439c573d600060003e3d6000fd5b3d604081183d60401002186105e0526105e0805180610260526020820181610280838360045afa5050505061026051602011615a5f57610280516102e05260206102c0526102c06020810151815160200360031b1c90506101e05261026051604011615a5f576102a0516102e05260206102c0526102c06020810151815160200360031b1c9050610200526020615af1600039600051638f8654c56102c05260206102c060046102dc845afa614457573d600060003e3d6000fd5b60203d10615a5f576102c09050516101c05118615a5f576020615af160003960005163ebcb00676102c0526101c0516102e05260206102c060246102dc845afa6144a6573d600060003e3d6000fd5b60203d10615a5f576102c09050516102205118615a5f576020615af16000396000516331f7e3066102c0526101c0516102e05260206102c060246102dc845afa6144f5573d600060003e3d6000fd5b60203d10615a5f576102c09050516102405118615a5f576101c05181526101e051602082015261020051604082015250565b600133602052600052604060002054156145a1576014610360527f4c6f616e20616c726561647920637265617465640000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b600461032051101561461357600f610360527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b603261032051111561468557600f610360527f4e656564206c657373207469636b7300000000000000000000000000000000006103805261036050610360518061038001601f826000031636823750506308c379a061032052602061034052601f19601f61036051011660440161033cfd5b6102e05161012052610300516101405261032051610160526146a8610380613830565b6103805161036052610360516103205160018103818111615a5f5790508060ff1c615a5f578082018281126000831218615a5f5790509050610380526146ef6103c0613549565b6103c0516103a05260013360205260005260406000206103005181556103a0516001820155506801000000000000000a546103c0526103c05160023360205260005260406000205568010000000000000006546103e052336103e05167fffffffffffffffe8111615a5f57600501556103e051680100000000000000053360205260005260406000205560016103e0510168010000000000000006556003546103a051808202811583838304141715615a5f57905090506004548015615a5f578082049050905061030051808201828110615a5f579050905061040052610400516003556103a0516004556020615af160003960005163ab047e006104205233610440526102e051610460526103605161048052610380516104a052803b15615a5f576000610420608461043c6000855af1614830573d600060003e3d6000fd5b50680100000000000000075461030051808201828110615a5f5790509050680100000000000000075561034051156148d5576102e0516040526102c051606052614878613efa565b6020615a9160003960005163a9059cbb61042052336104405261030051610460526020610420604461043c6000855af16148b7573d600060003e3d6000fd5b60203d10615a5f57610420518060011c615a5f576104805261048050505b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26102e051610420526103005161044052610360516104605261038051610480526103c0516104a05260a0610420a2337fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af6102e0516104205261030051610440526040610420a2565b604036610340376103005160a0526149786103806135e4565b61038080516103405260208101516103605250610340516149f9576012610380527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103a0526103805061038051806103a001601f826000031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b610340516102e051808201828110615a5f5790509050610340526020615af160003960005163b461100d6103c052610300516103e05260406103c060246103dc845afa614a4b573d600060003e3d6000fd5b60403d10615a5f576103c0905080516103805260208101516103a052506001610380516103a051030160008112615a5f576103c0526020615af160003960005163f3fef3a3610420526103005161044052670de0b6b3a7640000610460526040610420604461043c6000855af1614ac7573d600060003e3d6000fd5b60403d10615a5f57610420905080516103e052602081015161040052506103e05115614b5357601a610420527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104405261042050610420518061044001601f826000031636823750506308c379a06103e052602061040052601f19601f6104205101166044016103fcfd5b61032051614b7a57610400516102c051808201828110615a5f579050905061040052614b95565b610400516102c051808203828111615a5f5790509050610400525b610400516101205261034051610140526103c05161016052614bb8610440613830565b610440516104205261042051610380516103a051038082018281126000831218615a5f5790509050610440526020615af160003960005163ab047e00610460526103005161048052610400516104a052610420516104c052610440516104e052803b15615a5f576000610460608461047c6000855af1614c3d573d600060003e3d6000fd5b506001610300516020526000526040600020610340518155610360516001820155506801000000000000000a5461046052610460516002610300516020526000526040600020556102e05115614cdd5760035461036051808202811583838304141715615a5f57905090506004548015615a5f57808204905090506102e051808201828110615a5f57905090506104805261048051600355610360516004555b61032051614d2557610300517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af6102c051610480526102e0516104a0526040610480a2614d59565b610300517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb546102c051610480526020610480a25b610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26104005161048052610340516104a052610420516104c052610440516104e052610460516105005260a0610480a2565b680100000000000000065460018103818111615a5f5790506060526801000000000000000560405160205260005260406000205460805260405160805167fffffffffffffffe8111615a5f576005015418615a5f576000680100000000000000056040516020526000526040600020556060516080511015614e795760605167fffffffffffffffe8111615a5f576005015460a05260a05160805167fffffffffffffffe8111615a5f57600501556080516801000000000000000560a0516020526000526040600020555b6060516801000000000000000655565b606051614eed57601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f826000031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b670de0b6b3a764000060c05260a05115614f265760c05160a0518060ff1c615a5f578082038281136000831218615a5f579050905060c0525b6060518060ff1c615a5f576020615af16000396000516362ca4b1860e05260405161010052602060e0602460fc845afa614f65573d600060003e3d6000fd5b60203d10615a5f5760e09050518060ff1c615a5f5760c05180820281191515600160ff1b8414151782158484840514171615615a5f579050905005670de0b6b3a76400008103818113615a5f57905060c052608051156151ac576020615af160003960005163b461100d61012052604051610140526040610120602461013c845afa614ff6573d600060003e3d6000fd5b60403d10615a5f576101209050805160e052602081015161010052506020615af1600039600051638f8654c5610120526020610120600461013c845afa615042573d600060003e3d6000fd5b60203d10615a5f5761012090505160e05113156151ac576020615af16000396000516386fc88d3610180526020610180600461019c845afa615089573d600060003e3d6000fd5b60203d10615a5f57610180905051610160526020615af1600039600051632eb858e76101a05260e0516101c05260206101a060246101bc845afa6150d2573d600060003e3d6000fd5b60203d10615a5f576101a090505161018052610180516101605111156151ac5760c0516060516101605161018051808203828111615a5f57905090506020615af160003960005163544fb5c16101a0526040516101c05260406101a060246101bc845afa615145573d600060003e3d6000fd5b60403d10615a5f576101a0905060208101905051808202811583838304141715615a5f57905090506020615ad1600039600051808202811583838304141715615a5f5790509050048060ff1c615a5f578082018281126000831218615a5f579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff6040511161521957606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b6040366107c0376106405160a05261523a6108006135e4565b61080080516107c05260208101516107e05250610680511561530c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610640516040526107c05160605260016080526106805160a05261529c610800614e89565b61080051131561530c57600f610820527f4e6f7420656e6f7567682072656b7400000000000000000000000000000000006108405261082050610820518061084001601f826000031636823750506308c379a06107e052602061080052601f19601f6108205101166044016107fcfd5b6107c05161080052670de0b6b3a76400006107c0516106a051808202811583838304141715615a5f5790509050046107c0526107c05115615a5f576107c0516108005103610800526106a0516040526106805160605261536d6108406151b4565b61084051610820526020615af160003960005163f3fef3a361088052610640516108a052610820516108c0526040610880604461089c6000855af16153b7573d600060003e3d6000fd5b60403d10615a5f5761088090508051610840526020810151610860525061066051610840511015615448576008610880527f536c6970706167650000000000000000000000000000000000000000000000006108a0526108805061088051806108a001601f826000031636823750506308c379a061084052602061086052601f19601f61088051011660440161085cfd5b610840516107c051808281188284100218905090506108805261088051156154d6576020615a916000396000516323b872dd6108a0526020615af16000396000516108c052306108e052610880516109005260206108a060646108bc6000855af16154b8573d600060003e3d6000fd5b60203d10615a5f576108a0518060011c615a5f576109205261092050505b610840516107c051116155805733604052610860516060526106c0516080526154fd61405b565b6107c0516108405111156158f9576020615a916000396000516323b872dd6108a0526020615af16000396000516108c052336108e0526107c05161084051036109005260206108a060646108bc6000855af161555e573d600060003e3d6000fd5b60203d10615a5f576108a0518060011c615a5f576109205261092050506158f9565b610840516107c051036108a0526106e0516156155733604052610860516060526106c0516080526155af61405b565b6020615a916000396000516323b872dd6108c052336108e05230610900526108a0516109205260206108c060646108dc6000855af16155f3573d600060003e3d6000fd5b60203d10615a5f576108c0518060011c615a5f576109405261094050506158f9565b61086051156156ab576020615ab16000396000516323b872dd6108c0526020615af16000396000516108e0526106e05161090052610860516109205260206108c060646108dc6000855af161566f573d600060003e3d6000fd5b3d61568657803b15615a5f5760016109405261569f565b60203d10615a5f576108c0518060011c615a5f57610940525b61094090505115615a5f575b6106e0516040527f4ea696bb00000000000000000000000000000000000000000000000000000000606052610640516080526108405160a0526108605160c0526107c05160e0526107005180610100528060051b806101208261072060045afa5050506157196109206141d5565b61092080516108c05260208101516108e052604081015161090052506108a0516108e05110156157a9576013610920527f6e6f7420656e6f7567682070726f6365656473000000000000000000000000006109405261092050610920518061094001601f826000031636823750506308c379a06108e052602061090052601f19601f6109205101166044016108fcfd5b6108a0516108e0511115615821576020615a916000396000516323b872dd610920526106e0516109405233610960526108a0516108e05103610980526020610920606461093c6000855af1615803573d600060003e3d6000fd5b60203d10615a5f57610920518060011c615a5f576109a0526109a050505b6020615a916000396000516323b872dd610920526106e0516109405230610960526108a051610980526020610920606461093c6000855af1615868573d600060003e3d6000fd5b60203d10615a5f57610920518060011c615a5f576109a0526109a0505061090051156158f9576020615ab16000396000516323b872dd610920526106e05161094052336109605261090051610980526020610920606461093c6000855af16158d5573d600060003e3d6000fd5b60203d10615a5f57610920518060011c615a5f576109a0526109a090505115615a5f575b68010000000000000008546107c051808201828110615a5f5790509050680100000000000000085560016106405160205260005260406000206108005181556107e051600182015550610640517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d0610860516108a0526107c0516108c05260406108a0a261064051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d0610860516108a052610840516108c0526107c0516108e05260606108a0a361080051615a0a57610640517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366108a03760a06108a0a261064051604052615a0a614dae565b6003546107e051808202811583838304141715615a5f57905090506004548015615a5f57808204905090506108a0526107c0516108a0516107c05180828118828411021890509050036003556107e051600455565b600080fda165767970657283000307000b000000000000000000000000c9332fdcb1c491dcc683bae86fe3cb70360738bc000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000037417b2238aa52d0dd2d6252d989e728e8f706e400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000063000000000000000000000000000000000000000000000000003383482309faa60000000000000000000000000000000000000000000000000df29c916c5c292b0000000000000000000000000000000000000000000000000000000000000000

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

0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca00000000000000000000000001e7d3bf98d3f8d8ce193236c3e0ec4b00e32daae000000000000000000000000000000000000000000000000013fbe85edc9000000000000000000000000000000000000000000000000000000d529ae9e86000000000000000000000000000037417b2238aa52d0dd2d6252d989e728e8f706e4

-----Decoded View---------------
Arg [0] : collateral_token (address): 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
Arg [1] : monetary_policy (address): 0x1E7d3bf98d3f8D8CE193236c3e0eC4b00e32DaaE
Arg [2] : loan_discount (uint256): 90000000000000000
Arg [3] : liquidation_discount (uint256): 60000000000000000
Arg [4] : amm (address): 0x37417B2238AA52D0DD2D6252d989E728e8f706e4

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0
Arg [1] : 0000000000000000000000001e7d3bf98d3f8d8ce193236c3e0ec4b00e32daae
Arg [2] : 000000000000000000000000000000000000000000000000013fbe85edc90000
Arg [3] : 00000000000000000000000000000000000000000000000000d529ae9e860000
Arg [4] : 00000000000000000000000037417b2238aa52d0dd2d6252d989e728e8f706e4


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.