Overview
S Balance
S Value
$0.00Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match) This is an ERC-5202 Blueprint contract
Contract Name:
crvUSD Controller
Compiler Version
vyper:0.3.10
Contract Source Code (Vyper language format)
# @version 0.3.10
# pragma optimize codesize
# pragma evm-version shanghai
"""
@title crvUSD Controller
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2020-2024 - 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 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 MonetaryPolicy:
def rate_write() -> uint256: nonpayable
interface Factory:
def stablecoin() -> address: view
def admin() -> address: view
def fee_receiver() -> address: view
# Only if lending vault
def borrowed_token() -> address: view
def collateral_token() -> address: view
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 SetExtraHealth:
user: indexed(address)
health: uint256
event CollectFees:
amount: uint256
new_supply: uint256
event SetLMCallback:
callback: address
event Approval:
owner: indexed(address)
spender: indexed(address)
allow: bool
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)
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
MIN_TICKS_UINT: constant(uint256) = 4
MAX_SKIP_TICKS: constant(uint256) = 1024
MAX_P_BASE_BANDS: constant(int256) = 5
MAX_RATE: constant(uint256) = 43959106799 # 300% 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)
BORROWED_TOKEN: immutable(ERC20)
BORROWED_PRECISION: immutable(uint256)
AMM: immutable(LLAMMA)
A: immutable(uint256)
Aminus1: immutable(uint256)
LOGN_A_RATIO: immutable(int256) # log(A / (A - 1))
SQRT_BAND_RATIO: immutable(uint256)
MAX_ADMIN_FEE: constant(uint256) = 5 * 10**17 # 50%
MIN_FEE: constant(uint256) = 10**6 # 1e-12, still needs to be above 0
MAX_FEE: immutable(uint256) # let's set to MIN_TICKS / A: for example, 4% max fee for A=100
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)
CALLBACK_DEPOSIT_WITH_BYTES: constant(bytes4) = method_id("callback_deposit(address,uint256,uint256,uint256,uint256[],bytes)", output_type=bytes4)
# CALLBACK_REPAY_WITH_BYTES: constant(bytes4) = method_id("callback_repay(address,uint256,uint256,uint256,uint256[],bytes)", output_type=bytes4) <-- BUG! The reason is 0 at the beginning of method_id
CALLBACK_REPAY_WITH_BYTES: constant(bytes4) = 0x008ae188
CALLBACK_LIQUIDATE_WITH_BYTES: constant(bytes4) = method_id("callback_liquidate(address,uint256,uint256,uint256,uint256[],bytes)", output_type=bytes4)
DEAD_SHARES: constant(uint256) = 1000
approval: public(HashMap[address, HashMap[address, bool]])
extra_health: public(HashMap[address, uint256])
@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)
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 = unsafe_sub(_A, 1)
LOGN_A_RATIO = self.wad_ln(unsafe_div(_A * 10**18, unsafe_sub(_A, 1)))
MAX_FEE = min(unsafe_div(10**18 * MIN_TICKS, A), 10**17)
_collateral_token: ERC20 = ERC20(collateral_token)
_borrowed_token: ERC20 = empty(ERC20)
if collateral_token == empty(address):
# Lending vault factory
_collateral_token = ERC20(Factory(msg.sender).collateral_token())
_borrowed_token = ERC20(Factory(msg.sender).borrowed_token())
else:
# Stablecoin factory
# _collateral_token is already set
_borrowed_token = ERC20(Factory(msg.sender).stablecoin())
COLLATERAL_TOKEN = _collateral_token
BORROWED_TOKEN = _borrowed_token
COLLATERAL_PRECISION = pow_mod256(10, 18 - _collateral_token.decimals())
BORROWED_PRECISION = pow_mod256(10, 18 - _borrowed_token.decimals())
SQRT_BAND_RATIO = isqrt(unsafe_div(10**36 * _A, unsafe_sub(_A, 1)))
assert _borrowed_token.approve(msg.sender, max_value(uint256), default_return_value=True)
@internal
@pure
def _log_2(x: uint256) -> uint256:
"""
@dev An `internal` helper function that returns the log in base 2
of `x`, following the selected rounding direction.
@notice Note that it returns 0 if given 0. The implementation is
inspired by OpenZeppelin's implementation here:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.
This code is taken from snekmate.
@param x The 32-byte variable.
@return uint256 The 32-byte calculation result.
"""
value: uint256 = x
result: uint256 = empty(uint256)
# The following lines cannot overflow because we have the well-known
# decay behaviour of `log_2(max_value(uint256)) < max_value(uint256)`.
if (x >> 128 != empty(uint256)):
value = x >> 128
result = 128
if (value >> 64 != empty(uint256)):
value = value >> 64
result = unsafe_add(result, 64)
if (value >> 32 != empty(uint256)):
value = value >> 32
result = unsafe_add(result, 32)
if (value >> 16 != empty(uint256)):
value = value >> 16
result = unsafe_add(result, 16)
if (value >> 8 != empty(uint256)):
value = value >> 8
result = unsafe_add(result, 8)
if (value >> 4 != empty(uint256)):
value = value >> 4
result = unsafe_add(result, 4)
if (value >> 2 != empty(uint256)):
value = value >> 2
result = unsafe_add(result, 2)
if (value >> 1 != empty(uint256)):
result = unsafe_add(result, 1)
return result
@internal
@pure
def wad_ln(x: uint256) -> int256:
"""
@dev Calculates the natural logarithm of a signed integer with a
precision of 1e18.
@notice Note that it returns 0 if given 0. Furthermore, this function
consumes about 1,400 to 1,650 gas units depending on the value
of `x`. The implementation is inspired by Remco Bloemen's
implementation under the MIT license here:
https://xn--2-umb.com/22/exp-ln.
This code is taken from snekmate.
@param x The 32-byte variable.
@return int256 The 32-byte calculation result.
"""
value: int256 = convert(x, int256)
assert x > 0
# We want to convert `x` from "10 ** 18" fixed point to "2 ** 96"
# fixed point. We do this by multiplying by "2 ** 96 / 10 ** 18".
# But since "ln(x * C) = ln(x) + ln(C)" holds, we can just do nothing
# here and add "ln(2 ** 96 / 10 ** 18)" at the end.
# Reduce the range of `x` to "(1, 2) * 2 ** 96".
# Also remember that "ln(2 ** k * x) = k * ln(2) + ln(x)" holds.
k: int256 = unsafe_sub(convert(self._log_2(x), int256), 96)
# Note that to circumvent Vyper's safecast feature for the potentially
# negative expression `value <<= uint256(159 - k)`, we first convert the
# expression `value <<= uint256(159 - k)` to `bytes32` and subsequently
# to `uint256`. Remember that the EVM default behaviour is to use two's
# complement representation to handle signed integers.
value = convert(convert(convert(value << convert(unsafe_sub(159, k), uint256), bytes32), uint256) >> 159, int256)
# Evaluate using a "(8, 8)"-term rational approximation. Since `p` is monic,
# we will multiply by a scaling factor later.
p: int256 = unsafe_add(unsafe_mul(unsafe_add(value, 3_273_285_459_638_523_848_632_254_066_296), value) >> 96, 24_828_157_081_833_163_892_658_089_445_524)
p = unsafe_add(unsafe_mul(p, value) >> 96, 43_456_485_725_739_037_958_740_375_743_393)
p = unsafe_sub(unsafe_mul(p, value) >> 96, 11_111_509_109_440_967_052_023_855_526_967)
p = unsafe_sub(unsafe_mul(p, value) >> 96, 45_023_709_667_254_063_763_336_534_515_857)
p = unsafe_sub(unsafe_mul(p, value) >> 96, 14_706_773_417_378_608_786_704_636_184_526)
p = unsafe_sub(unsafe_mul(p, value), 795_164_235_651_350_426_258_249_787_498 << 96)
# We leave `p` in the "2 ** 192" base so that we do not have to scale it up
# again for the division. Note that `q` is monic by convention.
q: int256 = unsafe_add(unsafe_mul(unsafe_add(value, 5_573_035_233_440_673_466_300_451_813_936), value) >> 96, 71_694_874_799_317_883_764_090_561_454_958)
q = unsafe_add(unsafe_mul(q, value) >> 96, 283_447_036_172_924_575_727_196_451_306_956)
q = unsafe_add(unsafe_mul(q, value) >> 96, 401_686_690_394_027_663_651_624_208_769_553)
q = unsafe_add(unsafe_mul(q, value) >> 96, 204_048_457_590_392_012_362_485_061_816_622)
q = unsafe_add(unsafe_mul(q, value) >> 96, 31_853_899_698_501_571_402_653_359_427_138)
q = unsafe_add(unsafe_mul(q, value) >> 96, 909_429_971_244_387_300_277_376_558_375)
# It is known that the polynomial `q` has no zeros in the domain.
# No scaling is required, as `p` is already "2 ** 96" too large. Also,
# `r` is in the range "(0, 0.125) * 2 ** 96" after the division.
r: int256 = unsafe_div(p, q)
# To finalise the calculation, we have to proceed with the following steps:
# - multiply by the scaling factor "s = 5.549...",
# - add "ln(2 ** 96 / 10 ** 18)",
# - add "k * ln(2)", and
# - multiply by "10 ** 18 / 2 ** 96 = 5 ** 18 >> 78".
# In order to perform the most gas-efficient calculation, we carry out all
# these steps in one expression.
return unsafe_add(unsafe_add(unsafe_mul(r, 1_677_202_110_996_718_588_342_820_967_067_443_963_516_166),\
unsafe_mul(k, 16_597_577_552_685_614_221_487_285_958_193_947_469_193_820_559_219_878_177_908_093_499_208_371)),\
600_920_179_829_731_861_736_702_779_321_621_459_595_472_258_049_074_101_567_377_883_020_018_308) >> 174
@external
@pure
def factory() -> Factory:
"""
@notice Address of the factory
"""
return FACTORY
@external
@pure
def amm() -> LLAMMA:
"""
@notice Address of the AMM
"""
return AMM
@external
@pure
def collateral_token() -> ERC20:
"""
@notice Address of the collateral token
"""
return COLLATERAL_TOKEN
@external
@pure
def borrowed_token() -> ERC20:
"""
@notice Address of the borrowed token
"""
return BORROWED_TOKEN
@internal
def _save_rate():
"""
@notice Save current rate
"""
rate: uint256 = min(self.monetary_policy.rate_write(), MAX_RATE)
AMM.set_rate(rate)
@external
@nonreentrant('lock')
def save_rate():
"""
@notice Save current rate
"""
self._save_rate()
@internal
@view
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 = AMM.get_rate_mul()
loan: Loan = self.loan[user]
if loan.initial_debt == 0:
return (0, rate_mul)
else:
# Let user repay 1 smallest decimal more so that the system doesn't lose on precision
# Use ceil div
debt: uint256 = loan.initial_debt * rate_mul
if debt % loan.rate_mul > 0: # if only one loan -> don't have to do it
if self.n_loans > 1:
debt += unsafe_sub(loan.rate_mul, 1)
debt = unsafe_div(debt, loan.rate_mul) # loan.rate_mul is nonzero because we just had % successful
return (debt, 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(user)[0]
@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
@pure
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 = unsafe_div(
collateral * unsafe_sub(
10**18, min(discount + unsafe_div((DEAD_SHARES * 10**18), max(unsafe_div(collateral, N), DEAD_SHARES)), 10**18)
),
unsafe_mul(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, user: address) -> 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 + self.extra_health[user])
# 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 = unsafe_div(y_effective * p_base, debt * BORROWED_PRECISION + 1) # Now it's a ratio
# n1 = floor(log(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.wad_ln(y_effective)
if n1 < 0:
n1 -= unsafe_sub(LOGN_A_RATIO, 1) # This is to deal with vyper's rounding of negative numbers
n1 = unsafe_div(n1, LOGN_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 = self.wad_ln(AMM.get_base_price() * 10**18 / p_oracle)
if n1 < 0:
n1 -= LOGN_A_RATIO - 1 # This is to deal with vyper's rounding of negative numbers
n1 = unsafe_div(n1, LOGN_A_RATIO) + MAX_P_BASE_BANDS
n_min: int256 = AMM.active_band_with_skip()
n1 = max(n1, n_min + 1)
p_base: uint256 = AMM.p_oracle_up(n1)
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, current_debt: uint256 = 0, user: address = empty(address)) -> 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
@param current_debt Current debt of the user (if any)
@param user User to calculate the value for (only necessary for nonzero extra_health)
@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)
# if N < MIN_TICKS or N > MAX_TICKS:
assert N >= MIN_TICKS_UINT and N <= MAX_TICKS_UINT
y_effective: uint256 = self.get_y_effective(collateral * COLLATERAL_PRECISION, N,
self.loan_discount + self.extra_health[user])
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), unsafe_mul(10**18, BORROWED_PRECISION)) # Make it a bit smaller
return min(x, BORROWED_TOKEN.balanceOf(self) + current_debt) # Cannot borrow beyond the amount of coins Controller has
@external
@view
@nonreentrant('lock')
def min_collateral(debt: uint256, N: uint256, user: address = empty(address)) -> uint256:
"""
@notice Minimal amount of collateral required to support debt
@param debt The debt to support
@param N Number of bands to deposit into
@param user User to calculate the value for (only necessary for nonzero extra_health)
@return Minimal collateral required
"""
# Add N**2 to account for precision loss in multiple bands, e.g. N / (y/N) = N**2 / y
assert N <= MAX_TICKS_UINT and N >= MIN_TICKS_UINT
return unsafe_div(
unsafe_div(
debt * unsafe_mul(10**18, BORROWED_PRECISION) / self.max_p_base() * 10**18 / self.get_y_effective(10**18, N, self.loan_discount + self.extra_health[user]) + unsafe_add(unsafe_mul(N, unsafe_add(N, 2 * DEAD_SHARES)), unsafe_sub(COLLATERAL_PRECISION, 1)),
COLLATERAL_PRECISION
) * 10**18,
10**18 - 10**14)
@external
@view
@nonreentrant('lock')
def calculate_debt_n1(collateral: uint256, debt: uint256, N: uint256, user: address = empty(address)) -> 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
@param user User to calculate n1 for (only necessary for nonzero extra_health)
@return Upper band n1 (n1 <= n2) to deposit into. Signed integer
"""
return self._calculate_debt_n1(collateral, debt, N, user)
@internal
def transferFrom(token: ERC20, _from: address, _to: address, amount: uint256):
if amount > 0:
assert token.transferFrom(_from, _to, amount, default_return_value=True)
@internal
def transfer(token: ERC20, _to: address, amount: uint256):
if amount > 0:
assert token.transfer(_to, 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], callback_bytes: Bytes[10**4]) -> CallbackData:
assert callbacker != COLLATERAL_TOKEN.address
assert callbacker != BORROWED_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, callback_bytes)),
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(collateral: uint256, debt: uint256, N: uint256, transfer_coins: bool, _for: address):
assert self.loan[_for].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, _for)
n2: int256 = n1 + convert(unsafe_sub(N, 1), int256)
rate_mul: uint256 = AMM.get_rate_mul()
self.loan[_for] = Loan({initial_debt: debt, rate_mul: rate_mul})
liquidation_discount: uint256 = self.liquidation_discount
self.liquidation_discounts[_for] = liquidation_discount
n_loans: uint256 = self.n_loans
self.loans[n_loans] = _for
self.loan_ix[_for] = n_loans
self.n_loans = unsafe_add(n_loans, 1)
self._total_debt.initial_debt = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul + debt
self._total_debt.rate_mul = rate_mul
AMM.deposit_range(_for, collateral, n1, n2)
self.minted += debt
if transfer_coins:
self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral)
self.transfer(BORROWED_TOKEN, _for, debt)
self._save_rate()
log UserState(_for, collateral, debt, n1, n2, liquidation_discount)
log Borrow(_for, collateral, debt)
@external
@nonreentrant('lock')
def create_loan(collateral: uint256, debt: uint256, N: uint256, _for: address = msg.sender):
"""
@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
@param _for Address to create the loan for
"""
if _for != tx.origin:
# We can create a loan for tx.origin (for example when wrapping ETH with EOA),
# however need to approve in other cases
assert self._check_approval(_for)
self._create_loan(collateral, debt, N, True, _for)
@external
@nonreentrant('lock')
def create_loan_extended(collateral: uint256, debt: uint256, N: uint256, callbacker: address, callback_args: DynArray[uint256,5], callback_bytes: Bytes[10**4] = b"", _for: address = msg.sender):
"""
@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
@param _for Address to create the loan for
"""
if _for != tx.origin:
assert self._check_approval(_for)
# Before callback
self.transfer(BORROWED_TOKEN, callbacker, debt)
# For compatibility
callback_sig: bytes4 = CALLBACK_DEPOSIT_WITH_BYTES
if callback_bytes == b"":
callback_sig = CALLBACK_DEPOSIT
# Callback
# If there is any unused debt, callbacker can send it to the user
more_collateral: uint256 = self.execute_callback(
callbacker, callback_sig, _for, 0, collateral, debt, callback_args, callback_bytes).collateral
# After callback
self._create_loan(collateral + more_collateral, debt, N, False, _for)
self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral)
self.transferFrom(COLLATERAL_TOKEN, callbacker, AMM.address, more_collateral)
@internal
def _add_collateral_borrow(d_collateral: uint256, d_debt: uint256, _for: address, remove_collateral: bool,
check_rounding: 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
@param check_rounding Check that amount added is no less than the rounding error on the loan
"""
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
if check_rounding:
# We need d(x + p*y) > 1 wei. For that, we do an equivalent check (but with x2 for safety)
# This check is only needed when we add collateral for someone else, so gas is not an issue
# 2 * 10**(18 - borrow_decimals + collateral_decimals) =
# = 2 * 10**18 * 10**(18 - borrow_decimals) / 10**(collateral_decimals)
assert d_collateral * AMM.price_oracle() > 2 * 10**18 * BORROWED_PRECISION / COLLATERAL_PRECISION
n1: int256 = self._calculate_debt_n1(xy[1], debt, size, _for)
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 = 0
if _for == msg.sender:
liquidation_discount = self.liquidation_discount
self.liquidation_discounts[_for] = liquidation_discount
else:
liquidation_discount = self.liquidation_discounts[_for]
if d_debt != 0:
self._total_debt.initial_debt = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul + d_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)
@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, _for != msg.sender)
self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral)
self._save_rate()
@external
@nonreentrant('lock')
def remove_collateral(collateral: uint256, _for: address = msg.sender):
"""
@notice Remove some collateral without repaying the debt
@param collateral Amount of collateral to remove
@param _for Address to remove collateral for
"""
if collateral == 0:
return
assert self._check_approval(_for)
self._add_collateral_borrow(collateral, 0, _for, True, False)
self.transferFrom(COLLATERAL_TOKEN, AMM.address, _for, collateral)
self._save_rate()
@external
@nonreentrant('lock')
def borrow_more(collateral: uint256, debt: uint256, _for: address = msg.sender):
"""
@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
@param _for Address to borrow for
"""
if debt == 0:
return
assert self._check_approval(_for)
self._add_collateral_borrow(collateral, debt, _for, False, False)
self.minted += debt
self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral)
self.transfer(BORROWED_TOKEN, _for, debt)
self._save_rate()
@external
@nonreentrant('lock')
def borrow_more_extended(collateral: uint256, debt: uint256, callbacker: address, callback_args: DynArray[uint256,5], callback_bytes: Bytes[10**4] = b"", _for: address = msg.sender):
"""
@notice Borrow more stablecoins while adding more collateral using a callback (to leverage more)
@param collateral Amount of collateral to add
@param debt Amount of stablecoin debt to take
@param callbacker Address of the callback contract
@param callback_args Extra arguments for the callback (up to 5) such as min_amount etc
@param _for Address to borrow for
"""
if debt == 0:
return
assert self._check_approval(_for)
# Before callback
self.transfer(BORROWED_TOKEN, callbacker, debt)
# For compatibility
callback_sig: bytes4 = CALLBACK_DEPOSIT_WITH_BYTES
if callback_bytes == b"":
callback_sig = CALLBACK_DEPOSIT
# Callback
# If there is any unused debt, callbacker can send it to the user
more_collateral: uint256 = self.execute_callback(
callbacker, callback_sig, _for, 0, collateral, debt, callback_args, callback_bytes).collateral
# After callback
self._add_collateral_borrow(collateral + more_collateral, debt, _for, False, False)
self.minted += debt
self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral)
self.transferFrom(COLLATERAL_TOKEN, callbacker, AMM.address, more_collateral)
self._save_rate()
@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
@external
@nonreentrant('lock')
def repay(_d_debt: uint256, _for: address = msg.sender, max_active_band: int256 = 2**255-1):
"""
@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)
"""
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)
approval: bool = self._check_approval(_for)
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 approval
self.transferFrom(BORROWED_TOKEN, AMM.address, _for, xy[0])
if xy[1] > 0:
self.transferFrom(COLLATERAL_TOKEN, AMM.address, _for, xy[1])
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: int256 = unsafe_sub(ns[1], ns[0])
liquidation_discount: uint256 = self.liquidation_discounts[_for]
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, convert(unsafe_add(size, 1), uint256), _for)
n2: int256 = n1 + size
AMM.deposit_range(_for, xy[1], n1, n2)
if approval:
# Update liquidation discount only if we are that same user. No rugs
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
log UserState(_for, max_value(uint256), debt, ns[0], ns[1], liquidation_discount)
log Repay(_for, 0, d_debt)
if not approval:
# 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!
self.transferFrom(BORROWED_TOKEN, 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
self._save_rate()
@external
@nonreentrant('lock')
def repay_extended(callbacker: address, callback_args: DynArray[uint256,5], callback_bytes: Bytes[10**4] = b"", _for: address = msg.sender):
"""
@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
@param _for Address to repay for
"""
assert self._check_approval(_for)
# Before callback
ns: int256[2] = AMM.read_user_tick_numbers(_for)
xy: uint256[2] = AMM.withdraw(_for, 10**18)
debt: uint256 = 0
rate_mul: uint256 = 0
debt, rate_mul = self._debt(_for)
self.transferFrom(COLLATERAL_TOKEN, AMM.address, callbacker, xy[1])
# For compatibility
callback_sig: bytes4 = CALLBACK_REPAY_WITH_BYTES
if callback_bytes == b"":
callback_sig = CALLBACK_REPAY
cb: CallbackData = self.execute_callback(
callbacker, callback_sig, _for, xy[0], xy[1], debt, callback_args, callback_bytes)
# 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(_for)
# Transfer debt to self, everything else to _for
self.transferFrom(BORROWED_TOKEN, callbacker, self, cb.stablecoins)
self.transferFrom(BORROWED_TOKEN, AMM.address, self, xy[0])
if total_stablecoins > d_debt:
self.transfer(BORROWED_TOKEN, _for, unsafe_sub(total_stablecoins, d_debt))
self.transferFrom(COLLATERAL_TOKEN, callbacker, _for, cb.collateral)
log UserState(_for, 0, 0, 0, 0, 0)
# Else - partial repayment -> deleverage, but only if we are not underwater
else:
size: int256 = unsafe_sub(ns[1], ns[0])
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, convert(unsafe_add(size, 1), uint256), _for)
n2: int256 = n1 + size
AMM.deposit_range(_for, cb.collateral, n1, n2)
liquidation_discount: uint256 = self.liquidation_discount
self.liquidation_discounts[_for] = liquidation_discount
self.transferFrom(COLLATERAL_TOKEN, callbacker, AMM.address, cb.collateral)
# Stablecoin is all spent to repay debt -> all goes to self
self.transferFrom(BORROWED_TOKEN, callbacker, self, cb.stablecoins)
# We are above active band, so xy[0] is 0 anyway
log UserState(_for, cb.collateral, debt, n1, n2, liquidation_discount)
xy[1] -= cb.collateral
# No need to check _health() because it's the _for
# Common calls which we will do regardless of whether it's a full repay or not
log Repay(_for, xy[1], d_debt)
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
self._save_rate()
@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 - convert(liquidation_discount, int256)
health = unsafe_div(convert(AMM.get_x_down(user), int256) * health, convert(debt, int256)) - 10**18
if full:
ns0: int256 = AMM.read_user_tick_numbers(user)[0] # ns[1] > ns[0]
if ns0 > AMM.active_band(): # We are not in liquidation mode
p: uint256 = AMM.price_oracle()
p_up: uint256 = AMM.p_oracle_up(ns0)
if p > p_up:
health += convert(unsafe_div(unsafe_sub(p, p_up) * AMM.get_sum_xy(user)[1] * COLLATERAL_PRECISION, debt * BORROWED_PRECISION), 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
"""
ns: int256[2] = AMM.read_user_tick_numbers(user)
debt: int256 = convert(self._debt(user)[0], 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: # re-deposit
collateral = convert(AMM.get_sum_xy(user)[1], int256) + d_collateral
n1 = self._calculate_debt_n1(convert(collateral, uint256), convert(debt, uint256), n, user)
collateral *= convert(COLLATERAL_PRECISION, int256) # now has 18 decimals
else:
n1 = ns[0]
x_eff = convert(AMM.get_x_down(user) * unsafe_mul(10**18, BORROWED_PRECISION), int256)
debt *= convert(BORROWED_PRECISION, 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
@pure
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,
callbacker: address, callback_args: DynArray[uint256,5], callback_bytes: Bytes[10**4] = b""):
"""
@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 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 - 1), 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
xy: uint256[2] = AMM.withdraw(user, self._get_f_remove(frac, health_limit)) # [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)
self.transferFrom(BORROWED_TOKEN, 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.transferFrom(COLLATERAL_TOKEN, AMM.address, msg.sender, xy[1])
# Request what's left from user
self.transferFrom(BORROWED_TOKEN, msg.sender, self, to_repay)
else:
# Move collateral to callbacker, call it and remove everything from it back in
self.transferFrom(COLLATERAL_TOKEN, AMM.address, callbacker, xy[1])
# For compatibility
callback_sig: bytes4 = CALLBACK_LIQUIDATE_WITH_BYTES
if callback_bytes == b"":
callback_sig = CALLBACK_LIQUIDATE
# Callback
cb: CallbackData = self.execute_callback(
callbacker, callback_sig, user, xy[0], xy[1], debt, callback_args, callback_bytes)
assert cb.stablecoins >= to_repay, "not enough proceeds"
if cb.stablecoins > to_repay:
self.transferFrom(BORROWED_TOKEN, callbacker, msg.sender, unsafe_sub(cb.stablecoins, to_repay))
self.transferFrom(BORROWED_TOKEN, callbacker, self, to_repay)
self.transferFrom(COLLATERAL_TOKEN, callbacker, msg.sender, cb.collateral)
else:
# Withdraw collateral
self.transferFrom(COLLATERAL_TOKEN, AMM.address, msg.sender, xy[1])
# Return what's left to user
if xy[0] > debt:
self.transferFrom(BORROWED_TOKEN, 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
self._save_rate()
@external
@nonreentrant('lock')
def liquidate(user: address, min_x: uint256):
"""
@notice Perform 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)
"""
discount: uint256 = 0
if not self._check_approval(user):
discount = self.liquidation_discounts[user]
self._liquidate(user, min_x, discount, 10**18, empty(address), [])
@external
@nonreentrant('lock')
def liquidate_extended(user: address, min_x: uint256, frac: uint256,
callbacker: address, callback_args: DynArray[uint256,5], callback_bytes: Bytes[10**4] = b""):
"""
@notice Perform 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 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 not self._check_approval(user):
discount = self.liquidation_discounts[user]
self._liquidate(user, min_x, discount, min(frac, 10**18), callbacker, callback_args, callback_bytes)
@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 not self._check_approval(user):
health_limit = self.liquidation_discounts[user]
stablecoins: uint256 = unsafe_div(AMM.get_sum_xy(user)[0] * self._get_f_remove(frac, health_limit), 10 ** 18)
debt: uint256 = unsafe_div(self._debt(user)[0] * 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(user)[0], 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(user)[0]
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(user)[0], 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)
@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)
log SetLMCallback(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 + 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.
None of this fees are collected if factory has no fee_receiver - e.g. for lending
This is by design: lending does NOT earn interest, system makes money by using crvUSD
"""
# Calling fee_receiver will fail for lending markets because everything gets to lenders
_to: address = FACTORY.fee_receiver()
# Borrowing-based fees
rate_mul: uint256 = AMM.get_rate_mul()
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
self._save_rate()
# 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
self.transfer(BORROWED_TOKEN, _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
@external
@view
@nonreentrant('lock')
def check_lock() -> bool:
return True
# Allowance methods
@external
def approve(_spender: address, _allow: bool):
"""
@notice Allow another address to borrow and repay for the user
@param _spender Address to whitelist for the action
@param _allow Whether to turn the approval on or off (no amounts)
"""
self.approval[msg.sender][_spender] = _allow
log Approval(msg.sender, _spender, _allow)
@internal
@view
def _check_approval(_for: address) -> bool:
return msg.sender == _for or self.approval[_for][msg.sender]
@external
def set_extra_health(_value: uint256):
"""
@notice Add a little bit more to loan_discount to start SL with health higher than usual
@param _value 1e18-based addition to loan_discount
"""
self.extra_health[msg.sender] = _value
log SetExtraHealth(msg.sender, _value)Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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":"SetExtraHealth","inputs":[{"name":"user","type":"address","indexed":true},{"name":"health","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"},{"name":"SetLMCallback","inputs":[{"name":"callback","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"allow","type":"bool","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":"pure","type":"function","name":"factory","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"pure","type":"function","name":"amm","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"pure","type":"function","name":"collateral_token","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"pure","type":"function","name":"borrowed_token","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"nonpayable","type":"function","name":"save_rate","inputs":[],"outputs":[]},{"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":"max_borrowable","inputs":[{"name":"collateral","type":"uint256"},{"name":"N","type":"uint256"},{"name":"current_debt","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"max_borrowable","inputs":[{"name":"collateral","type":"uint256"},{"name":"N","type":"uint256"},{"name":"current_debt","type":"uint256"},{"name":"user","type":"address"}],"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":"min_collateral","inputs":[{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"},{"name":"user","type":"address"}],"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":"view","type":"function","name":"calculate_debt_n1","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"},{"name":"user","type":"address"}],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"nonpayable","type":"function","name":"create_loan","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"create_loan","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"},{"name":"_for","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","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":"nonpayable","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[]"},{"name":"callback_bytes","type":"bytes"}],"outputs":[]},{"stateMutability":"nonpayable","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[]"},{"name":"callback_bytes","type":"bytes"},{"name":"_for","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"add_collateral","inputs":[{"name":"collateral","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","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":"_for","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"borrow_more","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"borrow_more","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"_for","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"borrow_more_extended","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"borrow_more_extended","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"},{"name":"callback_bytes","type":"bytes"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"borrow_more_extended","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"},{"name":"callback_bytes","type":"bytes"},{"name":"_for","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"repay","inputs":[{"name":"_d_debt","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"repay","inputs":[{"name":"_d_debt","type":"uint256"},{"name":"_for","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"repay","inputs":[{"name":"_d_debt","type":"uint256"},{"name":"_for","type":"address"},{"name":"max_active_band","type":"int256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"repay_extended","inputs":[{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"repay_extended","inputs":[{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"},{"name":"callback_bytes","type":"bytes"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"repay_extended","inputs":[{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"},{"name":"callback_bytes","type":"bytes"},{"name":"_for","type":"address"}],"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_extended","inputs":[{"name":"user","type":"address"},{"name":"min_x","type":"uint256"},{"name":"frac","type":"uint256"},{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"liquidate_extended","inputs":[{"name":"user","type":"address"},{"name":"min_x","type":"uint256"},{"name":"frac","type":"uint256"},{"name":"callbacker","type":"address"},{"name":"callback_args","type":"uint256[]"},{"name":"callback_bytes","type":"bytes"}],"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_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":"check_lock","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"approve","inputs":[{"name":"_spender","type":"address"},{"name":"_allow","type":"bool"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_extra_health","inputs":[{"name":"_value","type":"uint256"}],"outputs":[]},{"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"}]},{"stateMutability":"view","type":"function","name":"approval","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"extra_health","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]}]Contract Creation Code
615fb03d81600a3d39f3fe71006158d75150346107e9576020615fad5f395f518060a01c6107e957610160526020615fcd5f395f518060a01c6107e95761018052602061602d5f395f518060a01c6107e9576101a0523361579752610180516801000000000000000855602061600d5f395f5168010000000000000009556020615fed5f395f516801000000000000000a55670de0b6b3a76400006004556101a051615837526101a05163f446c1d06101e05260206101e060046101fc845afa6100be573d5f5f3e3d5ffd5b60203d106107e9576101e09050516101c0526101c0516158575260016101c051036158775260016101c051036101c051670de0b6b3a7640000810281670de0b6b3a76400008204186107e95790500460a05261011b6101e061058b565b6101e0516158975261585751673782dace9d9000000467016345785d8a000081811867016345785d8a00008310021890506158d752610160516101e0525f61020052610160516101f45733632621db2f610220526020610220600461023c845afa610188573d5f5f3e3d5ffd5b60203d106107e957610220518060a01c6107e957610260526102609050516101e0523363765337b6610220526020610220600461023c845afa6101cd573d5f5f3e3d5ffd5b60203d106107e957610220518060a01c6107e957610260526102609050516102005261023a565b3363e9cbd822610220526020610220600461023c845afa610217573d5f5f3e3d5ffd5b60203d106107e957610220518060a01c6107e95761026052610260905051610200525b6101e0516157b752610200516157f7526101e05163313ce567610220526020610220600461023c845afa610270573d5f5f3e3d5ffd5b60203d106107e95761022090505180601203601281116107e9579050600a0a6157d7526102005163313ce567610220526020610220600461023c845afa6102b9573d5f5f3e3d5ffd5b60203d106107e95761022090505180601203601281116107e9579050600a0a6158175260016101c051036101c0516ec097ce7bc90715b34b9f10000000008102816ec097ce7bc90715b34b9f10000000008204186107e9579050048060b571010000000000000000000000000000000000821061033d578160801c91508060401b90505b6901000000000000000000821061035b578160401c91508060201b90505b650100000000008210610375578160201c91508060101b90505b6301000000821061038d578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090506158b7526102005163095ea7b36102205233610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610260526020610220604461023c5f855af1610449573d5f5f3e3d5ffd5b3d61046057803b156107e957600161028052610479565b60203d106107e957610220518060011c6107e957610280525b610280905051156107e9576157976107ed610000396158f7610000f35b6040516060525f60805260405160801c156104ba5760405160801c60605260806080525b60605160401c156104d85760605160401c6060526040608051016080525b60605160201c156104f65760605160201c6060526020608051016080525b60605160101c156105145760605160101c6060526010608051016080525b60605160081c156105325760605160081c6060526008608051016080525b60605160041c156105505760605160041c6060526004608051016080525b60605160021c1561056e5760605160021c6060526002608051016080525b60605160011c15610583576001608051016080525b608051815250565b60a0518060ff1c6107e95760c05260a051156107e957606060a0516040526105b4610100610496565b610100518060ff1c6107e9570360e05260c05160e051609f035f81126107e9571b609f1c8060ff1c6107e95760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b5f80fd5f3560e01c60056005600983060261553a01601b395f5160088160ff16838360181c0260181c0660031b8260081c61ffff1601601839505f51818160201c14600336111661004c57612fc9565b8061fffe16361034826001160217615536578060101c61ffff16565b6004358060a01c6155365760405260026040516020525f5260405f205460605260206060f35b60043567fffffffffffffffe8111615536576005015460405260206040f35b6004358060a01c61553657604052680100000000000000046040516020525f5260405f205460605260206060f35b680100000000000000055460405260206040f35b680100000000000000065460405260206040f35b680100000000000000075460405260206040f35b680100000000000000085460405260206040f35b680100000000000000095460405260206040f35b6801000000000000000a5460405260206040f35b6004358060a01c615536576040526024358060a01c615536576060526801000000000000000b6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6004358060a01c615536576040526801000000000000000c6040516020525f5260405f205460605260206060f35b602061579760403960206040f35b602061583760403960206040f35b60206157b760403960206040f35b60206157f760403960206040f35b5f546002146155365760025f55610219613320565b60035f55005b6004358060a01c6155365760e0525f5460021461553657602060e0516040526102496101006133ad565b610100f35b6004358060a01c615536576040525f546002146155365760016040516020525f5260405f2054151560605260206060f35b60206158375f395f5163095a0fc6606052602060606004607c845afa6102a7573d5f5f3e3d5ffd5b60203d1061553657606090505160405260035460605260045460805260605160405180820281158383830414171561553657905090506080518015615536578082049050905060a052602060a0f35b6040366102203761032a565b604435610220525f6102405261032a565b604435610220526064358060a01c61553657610240525b5f546002146155365760046024351015610344575f61034c565b603260243511155b156155365760043560206157d75f395f5180820281158383830414171561553657905090506040526024356060526801000000000000000a546801000000000000000c610240516020525f5260405f205480820182811061553657905090506080526103b961028061349a565b61028051610260526001670de0b6b3a7640000610260516103db6102a0613a2d565b6102a05180820281158383830414171561553657905090500460018181186001831102189050036102805260206158175f395f51670de0b6b3a76400000261028051670de05bc096e9c000810281670de05bc096e9c00082041861553657905004610280526102805160206157f75f395f516370a082316102a052306102c05260206102a060246102bc845afa610474573d5f5f3e3d5ffd5b60203d10615536576102a0905051610220518082018281106155365790509050808281188284100218905090506102e05260206102e0f35b5f610220526104c6565b6044358060a01c61553657610220525b5f5460021461553657603260243511156104e0575f6104e8565b600460243510155b1561553657670de05bc096e9c00060206157d75f395f5160043560206158175f395f51670de0b6b3a7640000028082028115838383041417156155365790509050610534610240613a2d565b6102405180156155365780820490509050670de0b6b3a7640000810281670de0b6b3a7640000820418615536579050670de0b6b3a76400006040526024356060526801000000000000000a546801000000000000000c610220516020525f5260405f205480820182811061553657905090506080526105b461026061349a565b6102605180156155365780820490509050600160206157d75f395f51036107d0602435016024350201808201828110615536579050905004670de0b6b3a7640000810281670de0b6b3a764000082041861553657905004610280526020610280f35b5f61032052610630565b6064358060a01c61553657610320525b5f546002146155365760206060600461016037610320516101c052610656610340613587565b610340f35b3361050052610675565b6064358060a01c61553657610500525b5f546002146155365760025f553261050051146106a8576105005160405261069e61052061453b565b6105205115615536575b6060600461032037600161038052610500516103a0526106c6614108565b60035f55005b5f617d20523361a4605261072f565b60a4356004016127108135116155365760208135018082617d203750503361a4605261072f565b60a4356004016127108135116155365760208135018082617d2037505060c4358060a01c6155365761a460525b6064358060a01c61553657617c4052608435600401600581351161553657803560208160051b018083617c60375050505f546002146155365760025f553261a46051146107925761a4605160405261078861a4a061453b565b61a4a05115615536575b60206157f7604039617c40516060526024356080526107af613d22565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610818577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa505061087261a4e0613d96565b61a4e06040810190505161a4c05260043561a4c05180820182811061553657905090506103205260406024610340375f6103805261a460516103a0526108b6614108565b60206157b760403933606052602061583760803960043560a0526108d8613ca6565b60206157b7604039617c4051606052602061583760803961a4c05160a0526108fe613ca6565b60035f55005b336105a05261091e565b6024358060a01c615536576105a0525b5f546002146155365760025f556004356109375761098d565b600435610320525f610340526105a051610360525f61038052336105a05114156103a052610963614575565b60206157b760403933606052602061583760803960043560a052610985613ca6565b61098d613320565b60035f55005b336105a0526109ad565b6024358060a01c615536576105a0525b5f546002146155365760025f556004356109c657610a35565b6105a0516040526109d86105c061453b565b6105c0511561553657600435610320525f610340526105a051610360526001610380525f6103a052610a08614575565b60206157b760403960206158376060396105a05160805260043560a052610a2d613ca6565b610a35613320565b60035f55005b336105a052610a55565b6044358060a01c615536576105a0525b5f546002146155365760025f55602435610a6e57610b16565b6105a051604052610a806105c061453b565b6105c051156155365760406004610320376105a0516103605260403661038037610aa8614575565b68010000000000000006546024358082018281106155365790509050680100000000000000065560206157b760403933606052602061583760803960043560a052610af1613ca6565b60206157f76040396105a051606052602435608052610b0e613d22565b610b16613320565b60035f55005b5f617d20523361a46052610b7f565b6084356004016127108135116155365760208135018082617d203750503361a46052610b7f565b6084356004016127108135116155365760208135018082617d2037505060a4358060a01c6155365761a460525b6044358060a01c61553657617c4052606435600401600581351161553657803560208160051b018083617c60375050505f546002146155365760025f55602435610bc857610d7f565b61a46051604052610bda61a4a061453b565b61a4a051156155365760206157f7604039617c4051606052602435608052610c00613d22565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610c69577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa5050610cc361a4e0613d96565b61a4e06040810190505161a4c05260043561a4c0518082018281106155365790509050610320526024356103405261a460516103605260403661038037610d08614575565b68010000000000000006546024358082018281106155365790509050680100000000000000065560206157b760403933606052602061583760803960043560a052610d51613ca6565b60206157b7604039617c4051606052602061583760803961a4c05160a052610d77613ca6565b610d7f613320565b60035f55005b33610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610e04565b6024358060a01c61553657610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610e04565b6024358060a01c6155365761032052604435610340525b5f546002146155365760025f55600435610e1d5761146f565b6040366103603761032051604052610e366103a06133ad565b6103a08051610360526020810151610380525061036051610eb65760126103a0527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103c0526103a0506103a051806103c001601f825f031636823750506308c379a061036052602061038052601f19601f6103a051011660440161037cfd5b61036051600435808281188284100218905090506103a0526103a05161036051036103605261032051604052610eed6103e061453b565b6103e0516103c052610360516110405760206158375f395f5163f3fef3a3610420526103205161044052670de0b6b3a7640000610460526040610420604461043c5f855af1610f3e573d5f5f3e3d5ffd5b60403d1061553657610420905060406103e060408360045afa50506103e05115610f91576103c051156155365760206157f76040396020615837606039610320516080526103e05160a052610f91613ca6565b6104005115610fc05760206157b76040396020615837606039610320516080526104005160a052610fc0613ca6565b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366104203760a0610420a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061040051610420526103a051610440526040610420a2610320516040526113ae614a6c566113ae565b60206158375f395f5163c16ef264610400526020610400600461041c845afa61106b573d5f5f3e3d5ffd5b60203d10615536576104009050516103e052610340516103e051136155365760206158375f395f5163b461100d6104405261032051610460526040610440602461045c845afa6110bd573d5f5f3e3d5ffd5b60403d10615536576104409050604061040060408360045afa5050610400516104205103610440526002610320516020525f5260405f2054610460526103e05161040051136111b357610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61048052610360516104a052610400516104c052610420516104e052610460516105005260a0610480a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610480526103a0516104a0526040610480a2611376565b60206158375f395f5163f3fef3a36104c052610320516104e052670de0b6b3a76400006105005260406104c060446104dc5f855af16111f4573d5f5f3e3d5ffd5b60403d10615536576104c09050604061048060408360045afa50506104a051610160526103605161018052600161044051015f8112615536576101a052610320516101c0526112446104e0613587565b6104e0516104c0526104c051610440518082018281125f83121861553657905090506104e05260206158375f395f5163ab047e006105005261032051610520526104a051610540526104c051610560526104e05161058052803b15615536575f610500608461051c5f855af16112bc573d5f5f3e3d5ffd5b506103c051156112ea57680100000000000000095461046052610460516002610320516020525f5260405f20555b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26104a0516105005261036051610520526104c051610540526104e05161056052610460516105805260a0610500a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610500526103a051610520526040610500a25b6103c0516113ae57600161032051604052610360516060525f6080526104605160a0526113a4610480614b40565b6104805112615536575b60206157f760403933606052306080526103a05160a0526113cd613ca6565b68010000000000000007546103a051808201828110615536579050905068010000000000000007556001610320516020525f5260405f2061036051815561038051600182015550600354610380518082028115838383041417156155365790509050600454801561553657808204905090506103e0526103a0516103e0516103a05180828118828411021890509050036003556103805160045561146f613320565b60035f55005b5f617d20523361a460526114d8565b6044356004016127108135116155365760208135018082617d203750503361a460526114d8565b6044356004016127108135116155365760208135018082617d203750506064358060a01c6155365761a460525b6004358060a01c61553657617c4052602435600401600581351161553657803560208160051b018083617c60375050505f546002146155365760025f5561a4605160405261152761a4a061453b565b61a4a051156155365760206158375f395f5163b461100d61a4e05261a4605161a50052604061a4e0602461a4fc845afa611563573d5f5f3e3d5ffd5b60403d106155365761a4e09050604061a4a060408360045afa505060206158375f395f5163f3fef3a361a5205261a4605161a54052670de0b6b3a764000061a56052604061a520604461a53c5f855af16115bf573d5f5f3e3d5ffd5b60403d106155365761a5209050604061a4e060408360045afa505060403661a5203761a460516040526115f361a5606133ad565b61a560805161a52052602081015161a540525060206157b76040396020615837606039617c405160805261a5005160a05261162c613ca6565b7e8ae1880000000000000000000000000000000000000000000000000000000061a560525f61a5a05261a5a0805160208201209050617d2051617d402018611694577fef67dc740000000000000000000000000000000000000000000000000000000061a560525b617c405160405261a5605160605261a4605160805261a4e05160a05261a5005160c05261a5205160e052617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506116f861a5e0613d96565b61a5e0606061a58060608360045afa505061a5a05161a4e051808201828110615536579050905061a5e05261a5e05115615536575f61a6005261a5205161a5e05110156118fa5761a4a05161a4c0510361a6205261a5805161a4a05113156155365761a5a05161a6005261a5a05161a520510361a5205261a5c0516101605261a5205161018052600161a62051015f8112615536576101a05261a460516101c0526117a461a660613587565b61a6605161a6405261a6405161a620518082018281125f831218615536579050905061a6605260206158375f395f5163ab047e0061a6805261a4605161a6a05261a5c05161a6c05261a6405161a6e05261a6605161a70052803b15615536575f61a680608461a69c5f855af161181c573d5f5f3e3d5ffd5b50680100000000000000095461a6805261a68051600261a460516020525f5260405f205560206157b7604039617c4051606052602061583760803961a5c05160a052611866613ca6565b60206157f7604039617c40516060523060805261a5a05160a052611888613ca6565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261a5c05161a6a05261a5205161a6c05261a6405161a6e05261a6605161a7005261a6805161a7205260a061a6a0a261a5005161a5c051808203828111615536579050905061a500526119e4565b61a5205161a600525f61a5205261a46051604052611916614a6c565b60206157f7604039617c40516060523060805261a5a05160a052611938613ca6565b60206157f760403960206158376060393060805261a4e05160a05261195b613ca6565b61a6005161a5e051111561198c5760206157f760403961a4605160605261a6005161a5e0510360805261198c613d22565b60206157b7604039617c405160605261a4605160805261a5c05160a0526119b1613ca6565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a6203760a061a620a25b61a460517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5005161a6205261a6005161a64052604061a620a2680100000000000000075461a6005180820182811061553657905090506801000000000000000755600161a460516020525f5260405f2061a52051815561a5405160018201555060035461a5405180820281158383830414171561553657905090506004548015615536578082049050905061a6205261a6005161a6205161a60051808281188284110218905090500360035561a54051600455611ac1613320565b60035f55005b5f61036052611ad9565b608435610360525b6004358060a01c61553657610320526064358060011c61553657610340525f546002146155365760206158375f395f5163b461100d6103c052610320516103e05260406103c060246103dc845afa611b33573d5f5f3e3d5ffd5b60403d10615536576103c09050604061038060408360045afa505061032051604052611b606103e06133ad565b6103e0518060ff1c615536576103c052610360516103e0525f610400526103c05115611bbe576002610320516020525f5260405f20548060ff1c61553657610400526001610380516103a05103015f8112615536576103e052611bfb565b68010000000000000009548060ff1c61553657610400527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610380525b606036610420376103c0516044358082018281125f83121861553657905090506103c05260016103c0511215611c90576011610480527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104a0526104805061048051806104a001601f825f031636823750506308c379a061044052602061046052601f19601f61048051011660440161045cfd5b60206158375f395f5163c16ef2646104a05260206104a060046104bc845afa611cbb573d5f5f3e3d5ffd5b60203d10615536576104a090505161048052610480516103805113611d5b57610380516104205260206158375f395f516362ca4b186104a052610320516104c05260206104a060246104bc845afa611d15573d5f5f3e3d5ffd5b60203d10615536576104a090505160206158175f395f51670de0b6b3a76400000280820281158383830414171561553657905090508060ff1c6155365761046052611e40565b60206158375f395f5163544fb5c16104a052610320516104c05260406104a060246104bc845afa611d8e573d5f5f3e3d5ffd5b60403d10615536576104a09050602081019050518060ff1c615536576024358082018281125f831218615536579050905061044052610440515f811261553657610160526103c0515f811261553657610180526103e0516101a052610320516101c052611dfc6104a0613587565b6104a051610420526104405160206157d75f395f518060ff1c6155365780820281191515600160ff1b84141517821584848405141716156155365790509050610440525b6103c05160206158175f395f518060ff1c6155365780820281191515600160ff1b841415178215848484051417161561553657905090506103c05260206158375f395f51632eb858e76104c052610420516104e05260206104c060246104dc845afa611eae573d5f5f3e3d5ffd5b60203d10615536576104c09050518060ff1c615536576104a05261048051610380511315611f3157610440515f8112615536576040526103e0516060525f608052611efa6104c061349a565b6104c0518060ff1c615536576104a05180820281191515600160ff1b84141517821584848405141716156155365790509050610460525b6103c05161046051056104c0526104c051670de0b6b3a76400006104c0516104005180820281191515600160ff1b84141517821584848405141716156155365790509050058082038281135f8312186155365790509050670de0b6b3a764000081038181136155365790506104c05261034051156120785761048051610420511315612078576104a05160206158375f395f516386fc88d3610500526020610500600461051c845afa611fe6573d5f5f3e3d5ffd5b60203d10615536576105009050518060ff1c61553657808281188284130218905090506104a0518082038281135f83121861553657905090506104e05260016104e05112612078576104c0516103c0516104e0516104405180820281191515600160ff1b84141517821584848405141716156155365790509050058082018281125f83121861553657905090506104c0525b60206104c0f35b6004358060a01c6155365761a6a0525f546002146155365760025f555f61a6c05261a6a0516040526120b261a6e061453b565b61a6e0516120cf57600261a6a0516020525f5260405f205461a6c0525b61a6a051617c4052602435617c605261a6c051617c8052670de0b6b3a7640000617ca052604036617cc0375f617da052612107614eb0565b60035f55005b5f61a7a052612135565b60a435600401612710813511615536576020813501808261a7a03750505b6004358060a01c6155365761a6a0526064358060a01c6155365761a6c052608435600401600581351161553657803560208160051b01808361a6e0375050505f546002146155365760025f555f61cf005261a6a05160405261219861cf2061453b565b61cf20516121b557600261a6a0516020525f5260405f205461cf00525b61a6a051617c4052602435617c605261cf0051617c8052604435670de0b6b3a7640000818118670de0b6b3a7640000831002189050617ca05261a6c051617cc05261a6e05160208160051b0180617ce08261a6e060045afa505050602061a7a0510180617da08261a7a060045afa505061222d614eb0565b60035f55005b670de0b6b3a76400006101005261224d565b602435610100525b6004358060a01c6155365760e0525f54600214615536575f6101205260e05160405261227a61014061453b565b6101405161229657600260e0516020525f5260405f2054610120525b670de0b6b3a764000060206158375f395f5163544fb5c16101605260e051610180526040610160602461017c845afa6122d1573d5f5f3e3d5ffd5b60403d106155365761016090505161010051604052610120516060526122f86101c0614e43565b6101c05180820281158383830414171561553657905090500461014052670de0b6b3a764000060e05160405261232f6101806133ad565b6101805161010051808202811583838304141715615536579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f35b5f6102005261238f565b6024358060011c61553657610200525b6004358060a01c615536576101e0525f546002146155365760206101e051610280526101e0516040526123c36102206133ad565b610220516102a052610200516102c05260026101e0516020525f5260405f20546102e05260806040608061028060045afa50612400610260614b40565b610260f35b6040366101e03761242b565b6004356101e0525f6102005261242b565b604060046101e0375b5f546002146155365768010000000000000005546102205261020051610240526102005161245c5761022051610240525b6101e051610260525f610280525f620f4240905b80620273a052610220516102605110156124945761024051620273a0511815612497565b60015b156124a157612609565b6102605167fffffffffffffffe81116155365760050154620273c052620273c0516040526124d1620274006133ad565b6202740051620273e052620273c051604052620273e05160605260016080526002620273c0516020525f5260405f205460a05261251062027420614b40565b620274205162027400527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6202740051136125e95760206158375f395f5163544fb5c16202746052620273c051620274805260406202746060246202747c845afa61257d573d5f5f3e3d5ffd5b60403d106155365762027460905060406202742060408360045afa5050610280516103e781116155365760a081026102a001620273c05181526202742051602082015262027440516040820152620273e051606082015262027400516080820152506001810161028052505b610260516001810181811061553657905061026052600101818118612470575b5050602080620273a05280620273a0015f6102805180835260a081025f826103e8811161553657801561266157905b60a081026102a00160a08202602088010160a08160a08460045afa505050600101818118612638575b50508201602001915050905081019050620273a0f35b602060206158375f395f5163f2388acb604052602060406004605c845afa6126a1573d5f5f3e3d5ffd5b60203d106155365760409050f35b6004358060a01c615536576040525f546002146155365760206158375f395f5163e8dd1ef1606052604051608052602060606024607c845afa6126f4573d5f5f3e3d5ffd5b60203d10615536576060518060011c6155365760a05260a0905051156155365760206158375f395f5163b461100d60a05260405160c052604060a0602460bc845afa612742573d5f5f3e3d5ffd5b60403d106155365760a090506040606060408360045afa505060206158375f395f51632eb858e760a05260605160c052602060a0602460bc845afa612789573d5f5f3e3d5ffd5b60203d106155365760a09050516101205260206158375f395f516324299b7a60e05260805161010052602060e0602460fc845afa6127c9573d5f5f3e3d5ffd5b60203d106155365760e0905051610140526040610120f35b6004358060a01c6155365760e0525f546002146155365760206158375f395f5163544fb5c16101405260e051610160526040610140602461015c845afa61282a573d5f5f3e3d5ffd5b60403d10615536576101409050604061010060408360045afa505060206158375f395f5163b461100d6101805260e0516101a0526040610180602461019c845afa612877573d5f5f3e3d5ffd5b60403d10615536576101809050604061014060408360045afa5050610120516101c052610100516101e05260e0516040526128b36101806133ad565b61018051610200526001610140516101605103015f8112615536576102205260806101c0f35b60206157975f395f5163f851a440604052602060406004605c845afa612901573d5f5f3e3d5ffd5b60203d10615536576040518060a01c61553657608052608090505133186155365760206158d75f395f51600435111561293a575f612944565b620f424060043510155b6129a35760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b60206158375f395f51631aa02d59604052600435606052803b15615536575f60406024605c5f855af16129d8573d5f5f3e3d5ffd5b50005b6004358060a01c615536576040525f546002146155365760025f5560206157975f395f5163f851a440606052602060606004607c845afa612a1e573d5f5f3e3d5ffd5b60203d10615536576060518060a01c6155365760a05260a0905051331861553657604051680100000000000000085560405163e91f2f4c606052602060606004607c5f855af1612a70573d5f5f3e3d5ffd5b60203d1061553657606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a160035f55005b5f546002146155365760025f5560206157975f395f5163f851a440604052602060406004605c845afa612ae3573d5f5f3e3d5ffd5b60203d10615536576040518060a01c615536576080526080905051331861553657602435600435111561553657662386f26fc1000060243510615536576706f05b59d3b20000600435116155365760243568010000000000000009556004356801000000000000000a557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a160035f55005b6004358060a01c615536576040525f546002146155365760025f5560206157975f395f5163f851a440606052602060606004607c845afa612bc3573d5f5f3e3d5ffd5b60203d10615536576060518060a01c6155365760a05260a090505133186155365760206158375f395f5163cc1891c7606052604051608052803b15615536575f60606024607c5f855af1612c19573d5f5f3e3d5ffd5b507fc770618faa286dffef5f2a2fea0c0685d5e0d18110242589ab70e5ae36f94ab560405160605260206060a160035f55005b60206158375f395f5163095a0fc6606052602060606004607c845afa612c74573d5f5f3e3d5ffd5b60203d1061553657606090505160405260035460605260045460805260605160405180820281158383830414171561553657905090506080518015615536578082049050905068010000000000000007548082018281106155365790509050606052680100000000000000065460a05260a05160605160a051808281188284110218905090500360c052602060c0f35b5f546002146155365760025f5560206157975f395f5163cab4d3db610140526020610140600461015c845afa612d3c573d5f5f3e3d5ffd5b60203d1061553657610140518060a01c61553657610180526101809050516101205260206158375f395f5163095a0fc6610160526020610160600461017c845afa612d89573d5f5f3e3d5ffd5b60203d10615536576101609050516101405260035461016052600454610180526101605161014051808202811583838304141715615536579050905061018051801561553657808204905090506101605261014051610180526101605160035561018051600455612df8613320565b61016051680100000000000000075480820182811061553657905090506101a05268010000000000000006546101c0526101c0516101a05111612e7c577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f5f6101e052610160516102005260406101e0a15f6101e05260206101e0612ef356612ef3565b6101a05168010000000000000006556101c0516101a051036101a05260206157f7604039610120516060526101a051608052612eb6613d22565b7f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f6101a0516101e052610160516102005260406101e0a160206101a05b60035f55f35b5f5460021461553657600160405260206040f35b6004358060a01c615536576040526024358060011c615536576060526060516801000000000000000b336020525f5260405f20806040516020525f5260405f20905055604051337f1d3e246ebbc933bf65d3290db9f93d67ab91a12d2b19308a35806e04d1c174c560605160805260206080a3005b6004356801000000000000000c336020525f5260405f2055337f06159bbd6e0c9c85edd120aebed689bc1c6bd79decfedbdf2f8c17abf681ca3c60043560405260206040a2005b5f5ffd5b6040516060525f60805260405160801c15612ff15760405160801c60605260806080525b60605160401c1561300f5760605160401c6060526040608051016080525b60605160201c1561302d5760605160201c6060526020608051016080525b60605160101c1561304b5760605160101c6060526010608051016080525b60605160081c156130695760605160081c6060526008608051016080525b60605160041c156130875760605160041c6060526004608051016080525b60605160021c156130a55760605160021c6060526002608051016080525b60605160011c156130ba576001608051016080525b608051815250565b60a0518060ff1c6155365760c05260a0511561553657606060a0516040526130eb610100612fcd565b610100518060ff1c615536570360e05260c05160e051609f035f8112615536571b609f1c8060ff1c6155365760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b680100000000000000085463e91f2f4c606052602060606004607c5f855af161334b573d5f5f3e3d5ffd5b60203d10615536576060905051640a3c2abcef818118640a3c2abcef83100218905060405260206158375f395f5163d4387a99606052604051608052602060606024607c5f855af161339f573d5f5f3e3d5ffd5b60203d106155365760605050565b60206158375f395f5163095a0fc6608052602060806004609c845afa6133d5573d5f5f3e3d5ffd5b60203d1061553657608090505160605260016040516020525f5260405f208054608052600181015460a0525060805161341d575f815260605160208201525061349856613498565b608051606051808202811583838304141715615536579050905060c05260c05160a051801561553657808206905090501561347f57600268010000000000000005541061347f5760c051600160a05103808201828110615536579050905060c0525b60a05160c0510460c05260c05181526060516020820152505b565b60605160206158b75f395f5102604051608051606051604051046103e88181186103e8831102189050683635c9adc5dea00000048082018281106155365790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a76400000380820281158383830414171561553657905090500460a05260a05160c052600160318101905b8060e05260605160e0511861353b5761357d565b60206158575f395f5160a05160206158775f395f5180820281158383830414171561553657905090500460a05260a05160c0510160c052600101818118613527575b505060c051815250565b610180516135f45760076101e0527f4e6f206c6f616e00000000000000000000000000000000000000000000000000610200526101e0506101e0518061020001601f825f031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b60206158375f395f51638f8654c5610200526020610200600461021c845afa61361f573d5f5f3e3d5ffd5b60203d10615536576102009050516101e05260206158375f395f51632eb858e7610220526101e051610240526020610220602461023c845afa613664573d5f5f3e3d5ffd5b60203d1061553657610220905051610200526101605160206157d75f395f5180820281158383830414171561553657905090506040526101a0516060526801000000000000000a546801000000000000000c6101c0516020525f5260405f205480820182811061553657905090506080526136e061024061349a565b61024051610220526101805160206158175f395f51808202811583838304141715615536579050905060018101818110615536579050610220516102005180820281158383830414171561553657905090500461022052610220516137a457600e610240527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006102605261024050610240518061026001601f825f031636823750506308c379a061020052602061022052601f19601f61024051011660440161021cfd5b6102205160a0526137b66102606130c2565b61026051610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610240511361380f5761024051600160206158975f395f51038082038281135f8312186155365790509050610240525b60206158975f395f51610240510561024052610240516101a0518060ff1c61553657806104000361040081135f831218615536579050808281188284120218905090506101e0518082018281125f8312186155365790509050610240526101e05161024051136139405760206158375f395f5163ec654706610260526102405160018103818113615536579050610280526020610260602461027c845afa6138b9573d5f5f3e3d5ffd5b60203d1061553657610260518060011c615536576102a0526102a090505161394057600d6102c0527f4465627420746f6f2068696768000000000000000000000000000000000000006102e0526102c0506102c051806102e001601f825f031636823750506308c379a06102805260206102a052601f19601f6102c051011660440161029cfd5b60206158375f395f516386fc88d36102a05260206102a060046102bc845afa61396b573d5f5f3e3d5ffd5b60203d10615536576102a090505160206158375f395f51632eb858e76102605261024051610280526020610260602461027c845afa6139ac573d5f5f3e3d5ffd5b60203d106155365761026090505110613a2457600d6102e0527f4465627420746f6f206869676800000000000000000000000000000000000000610300526102e0506102e0518061030001601f825f031636823750506308c379a06102a05260206102c052601f19601f6102e05101166044016102bcfd5b61024051815250565b60206158375f395f516386fc88d3610180526020610180600461019c845afa613a58573d5f5f3e3d5ffd5b60203d10615536576101809050516101605260206158375f395f5163a7db79a56101a05260206101a060046101bc845afa613a95573d5f5f3e3d5ffd5b60203d10615536576101a0905051670de0b6b3a7640000810281670de0b6b3a7640000820418615536579050610160518015615536578082049050905060a052613ae06101e06130c2565b6101e051610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113613b43576101805160206158975f395f51600181038181136155365790508082038281135f8312186155365790509050610180525b60206158975f395f516101805105600581018181126155365790506101805260206158375f395f5163c16ef2646101c05260206101c060046101dc845afa613b8d573d5f5f3e3d5ffd5b60203d10615536576101c09050516101a052610180516101a05160018101818112615536579050808281188284130218905090506101805260206158375f395f51632eb858e76101e052610180516102005260206101e060246101fc845afa613bf8573d5f5f3e3d5ffd5b60203d10615536576101e09050516101c0525f610401905b806101e0526101805160018103818113615536579050610180526101a0516101805113613c3c57613c9a565b6101c0516102005260206158775f395f516101c05160206158575f395f518082028115838383041417156155365790509050046101c052610160516101c0511115613c8f57610200518352505050613ca4565b600101818118613c10575b50506101c0518152505b565b60a05115613d20576040516323b872dd60c05260605160e0526080516101005260a05161012052602060c0606460dc5f855af1613ce5573d5f5f3e3d5ffd5b3d613cfc57803b1561553657600161014052613d14565b60203d106155365760c0518060011c61553657610140525b61014090505115615536575b565b60805115613d945760405163a9059cbb60a05260605160c05260805160e052602060a0604460bc5f855af1613d59573d5f5f3e3d5ffd5b3d613d7057803b1561553657600161010052613d88565b60203d106155365760a0518060011c61553657610100525b61010090505115615536575b565b60206157b75f395f51604051146155365760206157f75f395f5160405114615536576060366129003760206158375f395f51638f8654c5612960526020612960600461297c845afa613dea573d5f5f3e3d5ffd5b60203d10615536576129609050516129005260206158375f395f5163ebcb006761298052612900516129a0526020612980602461299c845afa613e2f573d5f5f3e3d5ffd5b60203d10615536576129809050516129605260206158375f395f516331f7e3066129a052612900516129c05260206129a060246129bc845afa613e74573d5f5f3e3d5ffd5b60203d10615536576129a0905051612980526040515a5f60605181615300015260048101905060c0608051612a205260a051612a405260c051612a605260e051612a805280612aa05280612a20015f610100518083528060051b5f8260058111615536578015613efe57905b8060051b61012001518160051b602088010152600101818118613ee0575b5050820160200191505090508101905080612ac05280612a200160206101c051018082826101c060045afa50508051806020830101601f825f03163682375050601f19601f82516020010116905081019050612a0052612a0080516020820183615300018281848460045afa50505080830192505050806152e0526152e050506040617c006152e0516153005f8686f190509050613f9e573d5f5f3e3d5ffd5b3d604081183d6040100218617be052617be06020815101806129a0828460045afa5050506129a051602011615536576129c051612a20526020612a0052612a006020810151815160200360031b1c9050612920526129a051604011615536576129e051612a20526020612a0052612a006020810151815160200360031b1c90506129405260206158375f395f51638f8654c5612a00526020612a006004612a1c845afa61404d573d5f5f3e3d5ffd5b60203d1061553657612a0090505161290051186155365760206158375f395f5163ebcb0067612a005261290051612a20526020612a006024612a1c845afa614097573d5f5f3e3d5ffd5b60203d1061553657612a0090505161296051186155365760206158375f395f516331f7e306612a005261290051612a20526020612a006024612a1c845afa6140e1573d5f5f3e3d5ffd5b60203d1061553657612a00905051612980511861553657606081606061290060045afa5050565b60016103a0516020525f5260405f2054156141825760146103c0527f4c6f616e20616c726561647920637265617465640000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b60046103605110156141f357600f6103c0527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b603261036051111561426457600f6103c0527f4e656564206c657373207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b61032051610160526103405161018052610360516101a0526103a0516101c05261428f6103e0613587565b6103e0516103c0526103c051600161036051038060ff1c615536578082018281125f83121861553657905090506103e05260206158375f395f5163095a0fc6610420526020610420600461043c845afa6142eb573d5f5f3e3d5ffd5b60203d10615536576104209050516104005260016103a0516020525f5260405f20610340518155610400516001820155506801000000000000000954610420526104205160026103a0516020525f5260405f20556801000000000000000554610440526103a0516104405167fffffffffffffffe8111615536576005015561044051680100000000000000046103a0516020525f5260405f2055600161044051016801000000000000000555600354610400518082028115838383041417156155365790509050600454801561553657808204905090506103405180820182811061553657905090506003556104005160045560206158375f395f5163ab047e00610460526103a05161048052610320516104a0526103c0516104c0526103e0516104e052803b15615536575f610460608461047c5f855af1614430573d5f5f3e3d5ffd5b506801000000000000000654610340518082018281106155365790509050680100000000000000065561038051156144a35760206157b76040393360605260206158376080396103205160a052614485613ca6565b60206157f76040396103a051606052610340516080526144a3613d22565b6144ab613320565b6103a0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610320516104605261034051610480526103c0516104a0526103e0516104c052610420516104e05260a0610460a26103a0517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516104605261034051610480526040610460a2565b604051331861454b576001614570565b6801000000000000000b6040516020525f5260405f2080336020525f5260405f209050545b815250565b6040366103c0376103605160405261458e6104006133ad565b61040080516103c05260208101516103e052506103c05161460e576012610400527f4c6f616e20646f65736e277420657869737400000000000000000000000000006104205261040050610400518061042001601f825f031636823750506308c379a06103c05260206103e052601f19601f6104005101166044016103dcfd5b6103c0516103405180820182811061553657905090506103c05260206158375f395f5163b461100d6104405261036051610460526040610440602461045c845afa61465b573d5f5f3e3d5ffd5b60403d10615536576104409050604061040060408360045afa50506001610400516104205103015f8112615536576104405260206158375f395f5163f3fef3a36104a052610360516104c052670de0b6b3a76400006104e05260406104a060446104bc5f855af16146ce573d5f5f3e3d5ffd5b60403d10615536576104a09050604061046060408360045afa5050610460511561475757601a6104a0527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104c0526104a0506104a051806104c001601f825f031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6103805161481b5761048051610320518082018281106155365790509050610480526103a051156148365760206158175f395f51671bc16d674ec80000810281671bc16d674ec8000082041861553657905060206157d75f395f51801561553657808204905090506103205160206158375f395f516386fc88d36104a05260206104a060046104bc845afa6147ee573d5f5f3e3d5ffd5b60203d10615536576104a09050518082028115838383041417156155365790509050111561553657614836565b61048051610320518082038281116155365790509050610480525b61048051610160526103c05161018052610440516101a052610360516101c0526148616104c0613587565b6104c0516104a0526104a0516104005161042051038082018281125f83121861553657905090506104c05260206158375f395f5163ab047e006104e052610360516105005261048051610520526104a051610540526104c05161056052803b15615536575f6104e060846104fc5f855af16148de573d5f5f3e3d5ffd5b506001610360516020525f5260405f206103c05181556103e0516001820155505f6104e0523361036051186149355768010000000000000009546104e0526104e0516002610360516020525f5260405f205561494a565b6002610360516020525f5260405f20546104e0525b610340511561499b576003546103e0518082028115838383041417156155365790509050600454801561553657808204905090506103405180820182811061553657905090506003556103e0516004555b610380516149e357610360517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516105005261034051610520526040610500a2614a17565b610360517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb5461032051610500526020610500a25b610360517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261048051610500526103c051610520526104a051610540526104c051610560526104e0516105805260a0610500a2565b680100000000000000055460018103818111615536579050606052680100000000000000046040516020525f5260405f205460805260405160805167fffffffffffffffe8111615536576005015418615536575f680100000000000000046040516020525f5260405f20556060516080511015614b305760605167fffffffffffffffe8111615536576005015460a05260a05160805167fffffffffffffffe811161553657600501556080516801000000000000000460a0516020525f5260405f20555b6060516801000000000000000555565b606051614ba357601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f825f031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60a0518060ff1c6155365780670de0b6b3a764000003670de0b6b3a764000081135f83121861553657905060c0526060518060ff1c6155365760206158375f395f516362ca4b1860e05260405161010052602060e0602460fc845afa614c0b573d5f5f3e3d5ffd5b60203d106155365760e09050518060ff1c6155365760c05180820281191515600160ff1b8414151782158484840514171615615536579050905005670de0b6b3a7640000810381811361553657905060c05260805115614e3b5760206158375f395f5163b461100d61010052604051610120526040610100602461011c845afa614c97573d5f5f3e3d5ffd5b60403d106155365761010090505160e05260206158375f395f51638f8654c5610100526020610100600461011c845afa614cd3573d5f5f3e3d5ffd5b60203d106155365761010090505160e0511315614e3b5760206158375f395f516386fc88d3610160526020610160600461017c845afa614d15573d5f5f3e3d5ffd5b60203d10615536576101609050516101405260206158375f395f51632eb858e76101805260e0516101a0526020610180602461019c845afa614d59573d5f5f3e3d5ffd5b60203d10615536576101809050516101605261016051610140511115614e3b5760c05160605160206158175f395f51808202811583838304141715615536579050905061016051610140510360206158375f395f5163544fb5c1610180526040516101a0526040610180602461019c845afa614dd7573d5f5f3e3d5ffd5b60403d1061553657610180905060208101905051808202811583838304141715615536579050905060206157d75f395f518082028115838383041417156155365790509050048060ff1c615536578082018281125f831218615536579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff60405111614ea857606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b60403661a4e037617c4051604052614ec961a5206133ad565b61a520805161a4e052602081015161a5005250617c805115614f9a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff617c405160405261a4e0516060526001608052617c805160a052614f2b61a520614b40565b61a520511315614f9a57600f61a540527f4e6f7420656e6f7567682072656b74000000000000000000000000000000000061a5605261a5405061a540518061a56001601f825f031636823750506308c379a061a50052602061a52052601f19601f61a54051011660440161a51cfd5b61a4e05161a52052670de0b6b3a764000061a4e051617ca0518082028115838383041417156155365790509050670de0b6b3a763ffff81018181106155365790500461a4e05261a4e051156155365761a4e05161a520510361a5205260206158375f395f5163f3fef3a361a5a052617c405161a5c052617ca051604052617c805160605261502961a580614e43565b61a5805161a5e052604061a5a0604461a5bc5f855af161504b573d5f5f3e3d5ffd5b60403d106155365761a5a09050604061a54060408360045afa5050617c605161a5405110156150d957600861a580527f536c69707061676500000000000000000000000000000000000000000000000061a5a05261a5805061a580518061a5a001601f825f031636823750506308c379a061a54052602061a56052601f19601f61a58051011660440161a55cfd5b61a5405161a4e0518082811882841002189050905061a5805260206157f760403960206158376060393060805261a5805160a052615115613ca6565b61a5405161a4e0511161517f5760206157b760403960206158376060393360805261a5605160a052615145613ca6565b61a4e05161a5405111156153ca5760206157f760403960206158376060393360805261a4e05161a540510360a0526153ca613ca6566153ca565b61a5405161a4e0510361a5a052617cc0516151da5760206157b760403960206158376060393360805261a5605160a0526151b7613ca6565b60206157f7604039336060523060805261a5a05160a0526153ca613ca6566153ca565b60206157b76040396020615837606039617cc05160805261a5605160a052615200613ca6565b7f750abc510000000000000000000000000000000000000000000000000000000061a5c0525f61a6005261a600805160208201209050617da051617dc02018615269577f4ea696bb0000000000000000000000000000000000000000000000000000000061a5c0525b617cc05160405261a5c051606052617c405160805261a5405160a05261a5605160c05261a4e05160e052617ce05160208160051b018061010082617ce060045afa5050506020617da05101806101c082617da060045afa50506152cd61a640613d96565b61a640606061a5e060608360045afa505061a5a05161a60051101561535157601361a640527f6e6f7420656e6f7567682070726f63656564730000000000000000000000000061a6605261a6405061a640518061a66001601f825f031636823750506308c379a061a60052602061a62052601f19601f61a64051011660440161a61cfd5b61a5a05161a6005111156153865760206157f7604039617cc0516060523360805261a5a05161a600510360a052615386613ca6565b60206157f7604039617cc0516060523060805261a5a05160a0526153a8613ca6565b60206157b7604039617cc0516060523360805261a6205160a0526153ca613ca6565b680100000000000000075461a4e051808201828110615536579050905068010000000000000007556001617c40516020525f5260405f2061a52051815561a50051600182015550617c40517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5605161a5a05261a4e05161a5c052604061a5a0a2617c4051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d061a5605161a5a05261a5405161a5c05261a4e05161a5e052606061a5a0a361a520516154d957617c40517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a5a03760a061a5a0a2617c40516040526154d9614a6c565b60035461a5005180820281158383830414171561553657905090506004548015615536578082049050905061a5a05261a4e05161a5a05161a4e051808281188284110218905090500360035561a50051600455615534613320565b565b5f80fd05dd55670a02a956170800b357470703e6570708005556df050003577f0301c25657090cf855b70c00b1569f08007c98ab24050005e4c4b3960b52010536b7dbb70b1c00a524049e57090e0045bcbaf487207f00454f02c42000ef00057463129a019e0025e2d8ebee237500254ba96d4606db01055457ff7b006800259a49719602f60045371fd8e60d8500252a94394501da0005e1ec3c68008e00253d140d212f0d0045e231bff001030005b4440df40ded0065627d2b83012b0005d14ff5b6099300254189617d28d9002589fa4dd707020125720fb25406160065546e040d22450045a21adb9e024e002581d2f1b729db00250b8db6811ac7008590f8667d242200457128f3b800ad0025a757320604ac00451cf1f94703020065fadc9bfb066500859b6c56ec021f00256cce39be00db0005adddfa940a4500656f972f1209040025c45a015501cc00052c5089c326af002522c714531ad100a520849f6a099d0045d0c581bf2ef9000531dc3ca8027f0005adfae4ce011700051b1800e32c4c0005cc1891c72b8000255794e890148400a55449b9cb013f00058908ea82237f004524977ef30b2b00e52621db2f01e80005e1270b6e015300457feb639514ab00c52a0c35862aae0045765337b601f60005acb708150db4004586eba68606200085d28952fc2117010551abf14a031300851b25cdaf22330025152f65cb1475006582bfb1de04b60065d9f11a6426770005bc61ea2306cc00c580e8f6ec241100251e0cfcef2d0400051e4b77602f82002569c6804e02040005fed9698c210d00c5dd171e7c0a3b0045ec74d0a827e1002523cfed03065b0065841957978a182d185018601840184818401828184018381818190160a16576797065728300030a0029
Deployed Bytecode
0xfe71006158d75150346107e9576020615fad5f395f518060a01c6107e957610160526020615fcd5f395f518060a01c6107e95761018052602061602d5f395f518060a01c6107e9576101a0523361579752610180516801000000000000000855602061600d5f395f5168010000000000000009556020615fed5f395f516801000000000000000a55670de0b6b3a76400006004556101a051615837526101a05163f446c1d06101e05260206101e060046101fc845afa6100be573d5f5f3e3d5ffd5b60203d106107e9576101e09050516101c0526101c0516158575260016101c051036158775260016101c051036101c051670de0b6b3a7640000810281670de0b6b3a76400008204186107e95790500460a05261011b6101e061058b565b6101e0516158975261585751673782dace9d9000000467016345785d8a000081811867016345785d8a00008310021890506158d752610160516101e0525f61020052610160516101f45733632621db2f610220526020610220600461023c845afa610188573d5f5f3e3d5ffd5b60203d106107e957610220518060a01c6107e957610260526102609050516101e0523363765337b6610220526020610220600461023c845afa6101cd573d5f5f3e3d5ffd5b60203d106107e957610220518060a01c6107e957610260526102609050516102005261023a565b3363e9cbd822610220526020610220600461023c845afa610217573d5f5f3e3d5ffd5b60203d106107e957610220518060a01c6107e95761026052610260905051610200525b6101e0516157b752610200516157f7526101e05163313ce567610220526020610220600461023c845afa610270573d5f5f3e3d5ffd5b60203d106107e95761022090505180601203601281116107e9579050600a0a6157d7526102005163313ce567610220526020610220600461023c845afa6102b9573d5f5f3e3d5ffd5b60203d106107e95761022090505180601203601281116107e9579050600a0a6158175260016101c051036101c0516ec097ce7bc90715b34b9f10000000008102816ec097ce7bc90715b34b9f10000000008204186107e9579050048060b571010000000000000000000000000000000000821061033d578160801c91508060401b90505b6901000000000000000000821061035b578160401c91508060201b90505b650100000000008210610375578160201c91508060101b90505b6301000000821061038d578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090506158b7526102005163095ea7b36102205233610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610260526020610220604461023c5f855af1610449573d5f5f3e3d5ffd5b3d61046057803b156107e957600161028052610479565b60203d106107e957610220518060011c6107e957610280525b610280905051156107e9576157976107ed610000396158f7610000f35b6040516060525f60805260405160801c156104ba5760405160801c60605260806080525b60605160401c156104d85760605160401c6060526040608051016080525b60605160201c156104f65760605160201c6060526020608051016080525b60605160101c156105145760605160101c6060526010608051016080525b60605160081c156105325760605160081c6060526008608051016080525b60605160041c156105505760605160041c6060526004608051016080525b60605160021c1561056e5760605160021c6060526002608051016080525b60605160011c15610583576001608051016080525b608051815250565b60a0518060ff1c6107e95760c05260a051156107e957606060a0516040526105b4610100610496565b610100518060ff1c6107e9570360e05260c05160e051609f035f81126107e9571b609f1c8060ff1c6107e95760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b5f80fd5f3560e01c60056005600983060261553a01601b395f5160088160ff16838360181c0260181c0660031b8260081c61ffff1601601839505f51818160201c14600336111661004c57612fc9565b8061fffe16361034826001160217615536578060101c61ffff16565b6004358060a01c6155365760405260026040516020525f5260405f205460605260206060f35b60043567fffffffffffffffe8111615536576005015460405260206040f35b6004358060a01c61553657604052680100000000000000046040516020525f5260405f205460605260206060f35b680100000000000000055460405260206040f35b680100000000000000065460405260206040f35b680100000000000000075460405260206040f35b680100000000000000085460405260206040f35b680100000000000000095460405260206040f35b6801000000000000000a5460405260206040f35b6004358060a01c615536576040526024358060a01c615536576060526801000000000000000b6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6004358060a01c615536576040526801000000000000000c6040516020525f5260405f205460605260206060f35b602061579760403960206040f35b602061583760403960206040f35b60206157b760403960206040f35b60206157f760403960206040f35b5f546002146155365760025f55610219613320565b60035f55005b6004358060a01c6155365760e0525f5460021461553657602060e0516040526102496101006133ad565b610100f35b6004358060a01c615536576040525f546002146155365760016040516020525f5260405f2054151560605260206060f35b60206158375f395f5163095a0fc6606052602060606004607c845afa6102a7573d5f5f3e3d5ffd5b60203d1061553657606090505160405260035460605260045460805260605160405180820281158383830414171561553657905090506080518015615536578082049050905060a052602060a0f35b6040366102203761032a565b604435610220525f6102405261032a565b604435610220526064358060a01c61553657610240525b5f546002146155365760046024351015610344575f61034c565b603260243511155b156155365760043560206157d75f395f5180820281158383830414171561553657905090506040526024356060526801000000000000000a546801000000000000000c610240516020525f5260405f205480820182811061553657905090506080526103b961028061349a565b61028051610260526001670de0b6b3a7640000610260516103db6102a0613a2d565b6102a05180820281158383830414171561553657905090500460018181186001831102189050036102805260206158175f395f51670de0b6b3a76400000261028051670de05bc096e9c000810281670de05bc096e9c00082041861553657905004610280526102805160206157f75f395f516370a082316102a052306102c05260206102a060246102bc845afa610474573d5f5f3e3d5ffd5b60203d10615536576102a0905051610220518082018281106155365790509050808281188284100218905090506102e05260206102e0f35b5f610220526104c6565b6044358060a01c61553657610220525b5f5460021461553657603260243511156104e0575f6104e8565b600460243510155b1561553657670de05bc096e9c00060206157d75f395f5160043560206158175f395f51670de0b6b3a7640000028082028115838383041417156155365790509050610534610240613a2d565b6102405180156155365780820490509050670de0b6b3a7640000810281670de0b6b3a7640000820418615536579050670de0b6b3a76400006040526024356060526801000000000000000a546801000000000000000c610220516020525f5260405f205480820182811061553657905090506080526105b461026061349a565b6102605180156155365780820490509050600160206157d75f395f51036107d0602435016024350201808201828110615536579050905004670de0b6b3a7640000810281670de0b6b3a764000082041861553657905004610280526020610280f35b5f61032052610630565b6064358060a01c61553657610320525b5f546002146155365760206060600461016037610320516101c052610656610340613587565b610340f35b3361050052610675565b6064358060a01c61553657610500525b5f546002146155365760025f553261050051146106a8576105005160405261069e61052061453b565b6105205115615536575b6060600461032037600161038052610500516103a0526106c6614108565b60035f55005b5f617d20523361a4605261072f565b60a4356004016127108135116155365760208135018082617d203750503361a4605261072f565b60a4356004016127108135116155365760208135018082617d2037505060c4358060a01c6155365761a460525b6064358060a01c61553657617c4052608435600401600581351161553657803560208160051b018083617c60375050505f546002146155365760025f553261a46051146107925761a4605160405261078861a4a061453b565b61a4a05115615536575b60206157f7604039617c40516060526024356080526107af613d22565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610818577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa505061087261a4e0613d96565b61a4e06040810190505161a4c05260043561a4c05180820182811061553657905090506103205260406024610340375f6103805261a460516103a0526108b6614108565b60206157b760403933606052602061583760803960043560a0526108d8613ca6565b60206157b7604039617c4051606052602061583760803961a4c05160a0526108fe613ca6565b60035f55005b336105a05261091e565b6024358060a01c615536576105a0525b5f546002146155365760025f556004356109375761098d565b600435610320525f610340526105a051610360525f61038052336105a05114156103a052610963614575565b60206157b760403933606052602061583760803960043560a052610985613ca6565b61098d613320565b60035f55005b336105a0526109ad565b6024358060a01c615536576105a0525b5f546002146155365760025f556004356109c657610a35565b6105a0516040526109d86105c061453b565b6105c0511561553657600435610320525f610340526105a051610360526001610380525f6103a052610a08614575565b60206157b760403960206158376060396105a05160805260043560a052610a2d613ca6565b610a35613320565b60035f55005b336105a052610a55565b6044358060a01c615536576105a0525b5f546002146155365760025f55602435610a6e57610b16565b6105a051604052610a806105c061453b565b6105c051156155365760406004610320376105a0516103605260403661038037610aa8614575565b68010000000000000006546024358082018281106155365790509050680100000000000000065560206157b760403933606052602061583760803960043560a052610af1613ca6565b60206157f76040396105a051606052602435608052610b0e613d22565b610b16613320565b60035f55005b5f617d20523361a46052610b7f565b6084356004016127108135116155365760208135018082617d203750503361a46052610b7f565b6084356004016127108135116155365760208135018082617d2037505060a4358060a01c6155365761a460525b6044358060a01c61553657617c4052606435600401600581351161553657803560208160051b018083617c60375050505f546002146155365760025f55602435610bc857610d7f565b61a46051604052610bda61a4a061453b565b61a4a051156155365760206157f7604039617c4051606052602435608052610c00613d22565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610c69577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa5050610cc361a4e0613d96565b61a4e06040810190505161a4c05260043561a4c0518082018281106155365790509050610320526024356103405261a460516103605260403661038037610d08614575565b68010000000000000006546024358082018281106155365790509050680100000000000000065560206157b760403933606052602061583760803960043560a052610d51613ca6565b60206157b7604039617c4051606052602061583760803961a4c05160a052610d77613ca6565b610d7f613320565b60035f55005b33610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610e04565b6024358060a01c61553657610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610e04565b6024358060a01c6155365761032052604435610340525b5f546002146155365760025f55600435610e1d5761146f565b6040366103603761032051604052610e366103a06133ad565b6103a08051610360526020810151610380525061036051610eb65760126103a0527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103c0526103a0506103a051806103c001601f825f031636823750506308c379a061036052602061038052601f19601f6103a051011660440161037cfd5b61036051600435808281188284100218905090506103a0526103a05161036051036103605261032051604052610eed6103e061453b565b6103e0516103c052610360516110405760206158375f395f5163f3fef3a3610420526103205161044052670de0b6b3a7640000610460526040610420604461043c5f855af1610f3e573d5f5f3e3d5ffd5b60403d1061553657610420905060406103e060408360045afa50506103e05115610f91576103c051156155365760206157f76040396020615837606039610320516080526103e05160a052610f91613ca6565b6104005115610fc05760206157b76040396020615837606039610320516080526104005160a052610fc0613ca6565b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366104203760a0610420a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061040051610420526103a051610440526040610420a2610320516040526113ae614a6c566113ae565b60206158375f395f5163c16ef264610400526020610400600461041c845afa61106b573d5f5f3e3d5ffd5b60203d10615536576104009050516103e052610340516103e051136155365760206158375f395f5163b461100d6104405261032051610460526040610440602461045c845afa6110bd573d5f5f3e3d5ffd5b60403d10615536576104409050604061040060408360045afa5050610400516104205103610440526002610320516020525f5260405f2054610460526103e05161040051136111b357610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61048052610360516104a052610400516104c052610420516104e052610460516105005260a0610480a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610480526103a0516104a0526040610480a2611376565b60206158375f395f5163f3fef3a36104c052610320516104e052670de0b6b3a76400006105005260406104c060446104dc5f855af16111f4573d5f5f3e3d5ffd5b60403d10615536576104c09050604061048060408360045afa50506104a051610160526103605161018052600161044051015f8112615536576101a052610320516101c0526112446104e0613587565b6104e0516104c0526104c051610440518082018281125f83121861553657905090506104e05260206158375f395f5163ab047e006105005261032051610520526104a051610540526104c051610560526104e05161058052803b15615536575f610500608461051c5f855af16112bc573d5f5f3e3d5ffd5b506103c051156112ea57680100000000000000095461046052610460516002610320516020525f5260405f20555b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26104a0516105005261036051610520526104c051610540526104e05161056052610460516105805260a0610500a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610500526103a051610520526040610500a25b6103c0516113ae57600161032051604052610360516060525f6080526104605160a0526113a4610480614b40565b6104805112615536575b60206157f760403933606052306080526103a05160a0526113cd613ca6565b68010000000000000007546103a051808201828110615536579050905068010000000000000007556001610320516020525f5260405f2061036051815561038051600182015550600354610380518082028115838383041417156155365790509050600454801561553657808204905090506103e0526103a0516103e0516103a05180828118828411021890509050036003556103805160045561146f613320565b60035f55005b5f617d20523361a460526114d8565b6044356004016127108135116155365760208135018082617d203750503361a460526114d8565b6044356004016127108135116155365760208135018082617d203750506064358060a01c6155365761a460525b6004358060a01c61553657617c4052602435600401600581351161553657803560208160051b018083617c60375050505f546002146155365760025f5561a4605160405261152761a4a061453b565b61a4a051156155365760206158375f395f5163b461100d61a4e05261a4605161a50052604061a4e0602461a4fc845afa611563573d5f5f3e3d5ffd5b60403d106155365761a4e09050604061a4a060408360045afa505060206158375f395f5163f3fef3a361a5205261a4605161a54052670de0b6b3a764000061a56052604061a520604461a53c5f855af16115bf573d5f5f3e3d5ffd5b60403d106155365761a5209050604061a4e060408360045afa505060403661a5203761a460516040526115f361a5606133ad565b61a560805161a52052602081015161a540525060206157b76040396020615837606039617c405160805261a5005160a05261162c613ca6565b7e8ae1880000000000000000000000000000000000000000000000000000000061a560525f61a5a05261a5a0805160208201209050617d2051617d402018611694577fef67dc740000000000000000000000000000000000000000000000000000000061a560525b617c405160405261a5605160605261a4605160805261a4e05160a05261a5005160c05261a5205160e052617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506116f861a5e0613d96565b61a5e0606061a58060608360045afa505061a5a05161a4e051808201828110615536579050905061a5e05261a5e05115615536575f61a6005261a5205161a5e05110156118fa5761a4a05161a4c0510361a6205261a5805161a4a05113156155365761a5a05161a6005261a5a05161a520510361a5205261a5c0516101605261a5205161018052600161a62051015f8112615536576101a05261a460516101c0526117a461a660613587565b61a6605161a6405261a6405161a620518082018281125f831218615536579050905061a6605260206158375f395f5163ab047e0061a6805261a4605161a6a05261a5c05161a6c05261a6405161a6e05261a6605161a70052803b15615536575f61a680608461a69c5f855af161181c573d5f5f3e3d5ffd5b50680100000000000000095461a6805261a68051600261a460516020525f5260405f205560206157b7604039617c4051606052602061583760803961a5c05160a052611866613ca6565b60206157f7604039617c40516060523060805261a5a05160a052611888613ca6565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261a5c05161a6a05261a5205161a6c05261a6405161a6e05261a6605161a7005261a6805161a7205260a061a6a0a261a5005161a5c051808203828111615536579050905061a500526119e4565b61a5205161a600525f61a5205261a46051604052611916614a6c565b60206157f7604039617c40516060523060805261a5a05160a052611938613ca6565b60206157f760403960206158376060393060805261a4e05160a05261195b613ca6565b61a6005161a5e051111561198c5760206157f760403961a4605160605261a6005161a5e0510360805261198c613d22565b60206157b7604039617c405160605261a4605160805261a5c05160a0526119b1613ca6565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a6203760a061a620a25b61a460517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5005161a6205261a6005161a64052604061a620a2680100000000000000075461a6005180820182811061553657905090506801000000000000000755600161a460516020525f5260405f2061a52051815561a5405160018201555060035461a5405180820281158383830414171561553657905090506004548015615536578082049050905061a6205261a6005161a6205161a60051808281188284110218905090500360035561a54051600455611ac1613320565b60035f55005b5f61036052611ad9565b608435610360525b6004358060a01c61553657610320526064358060011c61553657610340525f546002146155365760206158375f395f5163b461100d6103c052610320516103e05260406103c060246103dc845afa611b33573d5f5f3e3d5ffd5b60403d10615536576103c09050604061038060408360045afa505061032051604052611b606103e06133ad565b6103e0518060ff1c615536576103c052610360516103e0525f610400526103c05115611bbe576002610320516020525f5260405f20548060ff1c61553657610400526001610380516103a05103015f8112615536576103e052611bfb565b68010000000000000009548060ff1c61553657610400527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610380525b606036610420376103c0516044358082018281125f83121861553657905090506103c05260016103c0511215611c90576011610480527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104a0526104805061048051806104a001601f825f031636823750506308c379a061044052602061046052601f19601f61048051011660440161045cfd5b60206158375f395f5163c16ef2646104a05260206104a060046104bc845afa611cbb573d5f5f3e3d5ffd5b60203d10615536576104a090505161048052610480516103805113611d5b57610380516104205260206158375f395f516362ca4b186104a052610320516104c05260206104a060246104bc845afa611d15573d5f5f3e3d5ffd5b60203d10615536576104a090505160206158175f395f51670de0b6b3a76400000280820281158383830414171561553657905090508060ff1c6155365761046052611e40565b60206158375f395f5163544fb5c16104a052610320516104c05260406104a060246104bc845afa611d8e573d5f5f3e3d5ffd5b60403d10615536576104a09050602081019050518060ff1c615536576024358082018281125f831218615536579050905061044052610440515f811261553657610160526103c0515f811261553657610180526103e0516101a052610320516101c052611dfc6104a0613587565b6104a051610420526104405160206157d75f395f518060ff1c6155365780820281191515600160ff1b84141517821584848405141716156155365790509050610440525b6103c05160206158175f395f518060ff1c6155365780820281191515600160ff1b841415178215848484051417161561553657905090506103c05260206158375f395f51632eb858e76104c052610420516104e05260206104c060246104dc845afa611eae573d5f5f3e3d5ffd5b60203d10615536576104c09050518060ff1c615536576104a05261048051610380511315611f3157610440515f8112615536576040526103e0516060525f608052611efa6104c061349a565b6104c0518060ff1c615536576104a05180820281191515600160ff1b84141517821584848405141716156155365790509050610460525b6103c05161046051056104c0526104c051670de0b6b3a76400006104c0516104005180820281191515600160ff1b84141517821584848405141716156155365790509050058082038281135f8312186155365790509050670de0b6b3a764000081038181136155365790506104c05261034051156120785761048051610420511315612078576104a05160206158375f395f516386fc88d3610500526020610500600461051c845afa611fe6573d5f5f3e3d5ffd5b60203d10615536576105009050518060ff1c61553657808281188284130218905090506104a0518082038281135f83121861553657905090506104e05260016104e05112612078576104c0516103c0516104e0516104405180820281191515600160ff1b84141517821584848405141716156155365790509050058082018281125f83121861553657905090506104c0525b60206104c0f35b6004358060a01c6155365761a6a0525f546002146155365760025f555f61a6c05261a6a0516040526120b261a6e061453b565b61a6e0516120cf57600261a6a0516020525f5260405f205461a6c0525b61a6a051617c4052602435617c605261a6c051617c8052670de0b6b3a7640000617ca052604036617cc0375f617da052612107614eb0565b60035f55005b5f61a7a052612135565b60a435600401612710813511615536576020813501808261a7a03750505b6004358060a01c6155365761a6a0526064358060a01c6155365761a6c052608435600401600581351161553657803560208160051b01808361a6e0375050505f546002146155365760025f555f61cf005261a6a05160405261219861cf2061453b565b61cf20516121b557600261a6a0516020525f5260405f205461cf00525b61a6a051617c4052602435617c605261cf0051617c8052604435670de0b6b3a7640000818118670de0b6b3a7640000831002189050617ca05261a6c051617cc05261a6e05160208160051b0180617ce08261a6e060045afa505050602061a7a0510180617da08261a7a060045afa505061222d614eb0565b60035f55005b670de0b6b3a76400006101005261224d565b602435610100525b6004358060a01c6155365760e0525f54600214615536575f6101205260e05160405261227a61014061453b565b6101405161229657600260e0516020525f5260405f2054610120525b670de0b6b3a764000060206158375f395f5163544fb5c16101605260e051610180526040610160602461017c845afa6122d1573d5f5f3e3d5ffd5b60403d106155365761016090505161010051604052610120516060526122f86101c0614e43565b6101c05180820281158383830414171561553657905090500461014052670de0b6b3a764000060e05160405261232f6101806133ad565b6101805161010051808202811583838304141715615536579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f35b5f6102005261238f565b6024358060011c61553657610200525b6004358060a01c615536576101e0525f546002146155365760206101e051610280526101e0516040526123c36102206133ad565b610220516102a052610200516102c05260026101e0516020525f5260405f20546102e05260806040608061028060045afa50612400610260614b40565b610260f35b6040366101e03761242b565b6004356101e0525f6102005261242b565b604060046101e0375b5f546002146155365768010000000000000005546102205261020051610240526102005161245c5761022051610240525b6101e051610260525f610280525f620f4240905b80620273a052610220516102605110156124945761024051620273a0511815612497565b60015b156124a157612609565b6102605167fffffffffffffffe81116155365760050154620273c052620273c0516040526124d1620274006133ad565b6202740051620273e052620273c051604052620273e05160605260016080526002620273c0516020525f5260405f205460a05261251062027420614b40565b620274205162027400527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6202740051136125e95760206158375f395f5163544fb5c16202746052620273c051620274805260406202746060246202747c845afa61257d573d5f5f3e3d5ffd5b60403d106155365762027460905060406202742060408360045afa5050610280516103e781116155365760a081026102a001620273c05181526202742051602082015262027440516040820152620273e051606082015262027400516080820152506001810161028052505b610260516001810181811061553657905061026052600101818118612470575b5050602080620273a05280620273a0015f6102805180835260a081025f826103e8811161553657801561266157905b60a081026102a00160a08202602088010160a08160a08460045afa505050600101818118612638575b50508201602001915050905081019050620273a0f35b602060206158375f395f5163f2388acb604052602060406004605c845afa6126a1573d5f5f3e3d5ffd5b60203d106155365760409050f35b6004358060a01c615536576040525f546002146155365760206158375f395f5163e8dd1ef1606052604051608052602060606024607c845afa6126f4573d5f5f3e3d5ffd5b60203d10615536576060518060011c6155365760a05260a0905051156155365760206158375f395f5163b461100d60a05260405160c052604060a0602460bc845afa612742573d5f5f3e3d5ffd5b60403d106155365760a090506040606060408360045afa505060206158375f395f51632eb858e760a05260605160c052602060a0602460bc845afa612789573d5f5f3e3d5ffd5b60203d106155365760a09050516101205260206158375f395f516324299b7a60e05260805161010052602060e0602460fc845afa6127c9573d5f5f3e3d5ffd5b60203d106155365760e0905051610140526040610120f35b6004358060a01c6155365760e0525f546002146155365760206158375f395f5163544fb5c16101405260e051610160526040610140602461015c845afa61282a573d5f5f3e3d5ffd5b60403d10615536576101409050604061010060408360045afa505060206158375f395f5163b461100d6101805260e0516101a0526040610180602461019c845afa612877573d5f5f3e3d5ffd5b60403d10615536576101809050604061014060408360045afa5050610120516101c052610100516101e05260e0516040526128b36101806133ad565b61018051610200526001610140516101605103015f8112615536576102205260806101c0f35b60206157975f395f5163f851a440604052602060406004605c845afa612901573d5f5f3e3d5ffd5b60203d10615536576040518060a01c61553657608052608090505133186155365760206158d75f395f51600435111561293a575f612944565b620f424060043510155b6129a35760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b60206158375f395f51631aa02d59604052600435606052803b15615536575f60406024605c5f855af16129d8573d5f5f3e3d5ffd5b50005b6004358060a01c615536576040525f546002146155365760025f5560206157975f395f5163f851a440606052602060606004607c845afa612a1e573d5f5f3e3d5ffd5b60203d10615536576060518060a01c6155365760a05260a0905051331861553657604051680100000000000000085560405163e91f2f4c606052602060606004607c5f855af1612a70573d5f5f3e3d5ffd5b60203d1061553657606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a160035f55005b5f546002146155365760025f5560206157975f395f5163f851a440604052602060406004605c845afa612ae3573d5f5f3e3d5ffd5b60203d10615536576040518060a01c615536576080526080905051331861553657602435600435111561553657662386f26fc1000060243510615536576706f05b59d3b20000600435116155365760243568010000000000000009556004356801000000000000000a557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a160035f55005b6004358060a01c615536576040525f546002146155365760025f5560206157975f395f5163f851a440606052602060606004607c845afa612bc3573d5f5f3e3d5ffd5b60203d10615536576060518060a01c6155365760a05260a090505133186155365760206158375f395f5163cc1891c7606052604051608052803b15615536575f60606024607c5f855af1612c19573d5f5f3e3d5ffd5b507fc770618faa286dffef5f2a2fea0c0685d5e0d18110242589ab70e5ae36f94ab560405160605260206060a160035f55005b60206158375f395f5163095a0fc6606052602060606004607c845afa612c74573d5f5f3e3d5ffd5b60203d1061553657606090505160405260035460605260045460805260605160405180820281158383830414171561553657905090506080518015615536578082049050905068010000000000000007548082018281106155365790509050606052680100000000000000065460a05260a05160605160a051808281188284110218905090500360c052602060c0f35b5f546002146155365760025f5560206157975f395f5163cab4d3db610140526020610140600461015c845afa612d3c573d5f5f3e3d5ffd5b60203d1061553657610140518060a01c61553657610180526101809050516101205260206158375f395f5163095a0fc6610160526020610160600461017c845afa612d89573d5f5f3e3d5ffd5b60203d10615536576101609050516101405260035461016052600454610180526101605161014051808202811583838304141715615536579050905061018051801561553657808204905090506101605261014051610180526101605160035561018051600455612df8613320565b61016051680100000000000000075480820182811061553657905090506101a05268010000000000000006546101c0526101c0516101a05111612e7c577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f5f6101e052610160516102005260406101e0a15f6101e05260206101e0612ef356612ef3565b6101a05168010000000000000006556101c0516101a051036101a05260206157f7604039610120516060526101a051608052612eb6613d22565b7f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f6101a0516101e052610160516102005260406101e0a160206101a05b60035f55f35b5f5460021461553657600160405260206040f35b6004358060a01c615536576040526024358060011c615536576060526060516801000000000000000b336020525f5260405f20806040516020525f5260405f20905055604051337f1d3e246ebbc933bf65d3290db9f93d67ab91a12d2b19308a35806e04d1c174c560605160805260206080a3005b6004356801000000000000000c336020525f5260405f2055337f06159bbd6e0c9c85edd120aebed689bc1c6bd79decfedbdf2f8c17abf681ca3c60043560405260206040a2005b5f5ffd5b6040516060525f60805260405160801c15612ff15760405160801c60605260806080525b60605160401c1561300f5760605160401c6060526040608051016080525b60605160201c1561302d5760605160201c6060526020608051016080525b60605160101c1561304b5760605160101c6060526010608051016080525b60605160081c156130695760605160081c6060526008608051016080525b60605160041c156130875760605160041c6060526004608051016080525b60605160021c156130a55760605160021c6060526002608051016080525b60605160011c156130ba576001608051016080525b608051815250565b60a0518060ff1c6155365760c05260a0511561553657606060a0516040526130eb610100612fcd565b610100518060ff1c615536570360e05260c05160e051609f035f8112615536571b609f1c8060ff1c6155365760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b680100000000000000085463e91f2f4c606052602060606004607c5f855af161334b573d5f5f3e3d5ffd5b60203d10615536576060905051640a3c2abcef818118640a3c2abcef83100218905060405260206158375f395f5163d4387a99606052604051608052602060606024607c5f855af161339f573d5f5f3e3d5ffd5b60203d106155365760605050565b60206158375f395f5163095a0fc6608052602060806004609c845afa6133d5573d5f5f3e3d5ffd5b60203d1061553657608090505160605260016040516020525f5260405f208054608052600181015460a0525060805161341d575f815260605160208201525061349856613498565b608051606051808202811583838304141715615536579050905060c05260c05160a051801561553657808206905090501561347f57600268010000000000000005541061347f5760c051600160a05103808201828110615536579050905060c0525b60a05160c0510460c05260c05181526060516020820152505b565b60605160206158b75f395f5102604051608051606051604051046103e88181186103e8831102189050683635c9adc5dea00000048082018281106155365790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a76400000380820281158383830414171561553657905090500460a05260a05160c052600160318101905b8060e05260605160e0511861353b5761357d565b60206158575f395f5160a05160206158775f395f5180820281158383830414171561553657905090500460a05260a05160c0510160c052600101818118613527575b505060c051815250565b610180516135f45760076101e0527f4e6f206c6f616e00000000000000000000000000000000000000000000000000610200526101e0506101e0518061020001601f825f031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b60206158375f395f51638f8654c5610200526020610200600461021c845afa61361f573d5f5f3e3d5ffd5b60203d10615536576102009050516101e05260206158375f395f51632eb858e7610220526101e051610240526020610220602461023c845afa613664573d5f5f3e3d5ffd5b60203d1061553657610220905051610200526101605160206157d75f395f5180820281158383830414171561553657905090506040526101a0516060526801000000000000000a546801000000000000000c6101c0516020525f5260405f205480820182811061553657905090506080526136e061024061349a565b61024051610220526101805160206158175f395f51808202811583838304141715615536579050905060018101818110615536579050610220516102005180820281158383830414171561553657905090500461022052610220516137a457600e610240527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006102605261024050610240518061026001601f825f031636823750506308c379a061020052602061022052601f19601f61024051011660440161021cfd5b6102205160a0526137b66102606130c2565b61026051610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610240511361380f5761024051600160206158975f395f51038082038281135f8312186155365790509050610240525b60206158975f395f51610240510561024052610240516101a0518060ff1c61553657806104000361040081135f831218615536579050808281188284120218905090506101e0518082018281125f8312186155365790509050610240526101e05161024051136139405760206158375f395f5163ec654706610260526102405160018103818113615536579050610280526020610260602461027c845afa6138b9573d5f5f3e3d5ffd5b60203d1061553657610260518060011c615536576102a0526102a090505161394057600d6102c0527f4465627420746f6f2068696768000000000000000000000000000000000000006102e0526102c0506102c051806102e001601f825f031636823750506308c379a06102805260206102a052601f19601f6102c051011660440161029cfd5b60206158375f395f516386fc88d36102a05260206102a060046102bc845afa61396b573d5f5f3e3d5ffd5b60203d10615536576102a090505160206158375f395f51632eb858e76102605261024051610280526020610260602461027c845afa6139ac573d5f5f3e3d5ffd5b60203d106155365761026090505110613a2457600d6102e0527f4465627420746f6f206869676800000000000000000000000000000000000000610300526102e0506102e0518061030001601f825f031636823750506308c379a06102a05260206102c052601f19601f6102e05101166044016102bcfd5b61024051815250565b60206158375f395f516386fc88d3610180526020610180600461019c845afa613a58573d5f5f3e3d5ffd5b60203d10615536576101809050516101605260206158375f395f5163a7db79a56101a05260206101a060046101bc845afa613a95573d5f5f3e3d5ffd5b60203d10615536576101a0905051670de0b6b3a7640000810281670de0b6b3a7640000820418615536579050610160518015615536578082049050905060a052613ae06101e06130c2565b6101e051610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113613b43576101805160206158975f395f51600181038181136155365790508082038281135f8312186155365790509050610180525b60206158975f395f516101805105600581018181126155365790506101805260206158375f395f5163c16ef2646101c05260206101c060046101dc845afa613b8d573d5f5f3e3d5ffd5b60203d10615536576101c09050516101a052610180516101a05160018101818112615536579050808281188284130218905090506101805260206158375f395f51632eb858e76101e052610180516102005260206101e060246101fc845afa613bf8573d5f5f3e3d5ffd5b60203d10615536576101e09050516101c0525f610401905b806101e0526101805160018103818113615536579050610180526101a0516101805113613c3c57613c9a565b6101c0516102005260206158775f395f516101c05160206158575f395f518082028115838383041417156155365790509050046101c052610160516101c0511115613c8f57610200518352505050613ca4565b600101818118613c10575b50506101c0518152505b565b60a05115613d20576040516323b872dd60c05260605160e0526080516101005260a05161012052602060c0606460dc5f855af1613ce5573d5f5f3e3d5ffd5b3d613cfc57803b1561553657600161014052613d14565b60203d106155365760c0518060011c61553657610140525b61014090505115615536575b565b60805115613d945760405163a9059cbb60a05260605160c05260805160e052602060a0604460bc5f855af1613d59573d5f5f3e3d5ffd5b3d613d7057803b1561553657600161010052613d88565b60203d106155365760a0518060011c61553657610100525b61010090505115615536575b565b60206157b75f395f51604051146155365760206157f75f395f5160405114615536576060366129003760206158375f395f51638f8654c5612960526020612960600461297c845afa613dea573d5f5f3e3d5ffd5b60203d10615536576129609050516129005260206158375f395f5163ebcb006761298052612900516129a0526020612980602461299c845afa613e2f573d5f5f3e3d5ffd5b60203d10615536576129809050516129605260206158375f395f516331f7e3066129a052612900516129c05260206129a060246129bc845afa613e74573d5f5f3e3d5ffd5b60203d10615536576129a0905051612980526040515a5f60605181615300015260048101905060c0608051612a205260a051612a405260c051612a605260e051612a805280612aa05280612a20015f610100518083528060051b5f8260058111615536578015613efe57905b8060051b61012001518160051b602088010152600101818118613ee0575b5050820160200191505090508101905080612ac05280612a200160206101c051018082826101c060045afa50508051806020830101601f825f03163682375050601f19601f82516020010116905081019050612a0052612a0080516020820183615300018281848460045afa50505080830192505050806152e0526152e050506040617c006152e0516153005f8686f190509050613f9e573d5f5f3e3d5ffd5b3d604081183d6040100218617be052617be06020815101806129a0828460045afa5050506129a051602011615536576129c051612a20526020612a0052612a006020810151815160200360031b1c9050612920526129a051604011615536576129e051612a20526020612a0052612a006020810151815160200360031b1c90506129405260206158375f395f51638f8654c5612a00526020612a006004612a1c845afa61404d573d5f5f3e3d5ffd5b60203d1061553657612a0090505161290051186155365760206158375f395f5163ebcb0067612a005261290051612a20526020612a006024612a1c845afa614097573d5f5f3e3d5ffd5b60203d1061553657612a0090505161296051186155365760206158375f395f516331f7e306612a005261290051612a20526020612a006024612a1c845afa6140e1573d5f5f3e3d5ffd5b60203d1061553657612a00905051612980511861553657606081606061290060045afa5050565b60016103a0516020525f5260405f2054156141825760146103c0527f4c6f616e20616c726561647920637265617465640000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b60046103605110156141f357600f6103c0527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b603261036051111561426457600f6103c0527f4e656564206c657373207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b61032051610160526103405161018052610360516101a0526103a0516101c05261428f6103e0613587565b6103e0516103c0526103c051600161036051038060ff1c615536578082018281125f83121861553657905090506103e05260206158375f395f5163095a0fc6610420526020610420600461043c845afa6142eb573d5f5f3e3d5ffd5b60203d10615536576104209050516104005260016103a0516020525f5260405f20610340518155610400516001820155506801000000000000000954610420526104205160026103a0516020525f5260405f20556801000000000000000554610440526103a0516104405167fffffffffffffffe8111615536576005015561044051680100000000000000046103a0516020525f5260405f2055600161044051016801000000000000000555600354610400518082028115838383041417156155365790509050600454801561553657808204905090506103405180820182811061553657905090506003556104005160045560206158375f395f5163ab047e00610460526103a05161048052610320516104a0526103c0516104c0526103e0516104e052803b15615536575f610460608461047c5f855af1614430573d5f5f3e3d5ffd5b506801000000000000000654610340518082018281106155365790509050680100000000000000065561038051156144a35760206157b76040393360605260206158376080396103205160a052614485613ca6565b60206157f76040396103a051606052610340516080526144a3613d22565b6144ab613320565b6103a0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610320516104605261034051610480526103c0516104a0526103e0516104c052610420516104e05260a0610460a26103a0517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516104605261034051610480526040610460a2565b604051331861454b576001614570565b6801000000000000000b6040516020525f5260405f2080336020525f5260405f209050545b815250565b6040366103c0376103605160405261458e6104006133ad565b61040080516103c05260208101516103e052506103c05161460e576012610400527f4c6f616e20646f65736e277420657869737400000000000000000000000000006104205261040050610400518061042001601f825f031636823750506308c379a06103c05260206103e052601f19601f6104005101166044016103dcfd5b6103c0516103405180820182811061553657905090506103c05260206158375f395f5163b461100d6104405261036051610460526040610440602461045c845afa61465b573d5f5f3e3d5ffd5b60403d10615536576104409050604061040060408360045afa50506001610400516104205103015f8112615536576104405260206158375f395f5163f3fef3a36104a052610360516104c052670de0b6b3a76400006104e05260406104a060446104bc5f855af16146ce573d5f5f3e3d5ffd5b60403d10615536576104a09050604061046060408360045afa5050610460511561475757601a6104a0527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104c0526104a0506104a051806104c001601f825f031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6103805161481b5761048051610320518082018281106155365790509050610480526103a051156148365760206158175f395f51671bc16d674ec80000810281671bc16d674ec8000082041861553657905060206157d75f395f51801561553657808204905090506103205160206158375f395f516386fc88d36104a05260206104a060046104bc845afa6147ee573d5f5f3e3d5ffd5b60203d10615536576104a09050518082028115838383041417156155365790509050111561553657614836565b61048051610320518082038281116155365790509050610480525b61048051610160526103c05161018052610440516101a052610360516101c0526148616104c0613587565b6104c0516104a0526104a0516104005161042051038082018281125f83121861553657905090506104c05260206158375f395f5163ab047e006104e052610360516105005261048051610520526104a051610540526104c05161056052803b15615536575f6104e060846104fc5f855af16148de573d5f5f3e3d5ffd5b506001610360516020525f5260405f206103c05181556103e0516001820155505f6104e0523361036051186149355768010000000000000009546104e0526104e0516002610360516020525f5260405f205561494a565b6002610360516020525f5260405f20546104e0525b610340511561499b576003546103e0518082028115838383041417156155365790509050600454801561553657808204905090506103405180820182811061553657905090506003556103e0516004555b610380516149e357610360517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516105005261034051610520526040610500a2614a17565b610360517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb5461032051610500526020610500a25b610360517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261048051610500526103c051610520526104a051610540526104c051610560526104e0516105805260a0610500a2565b680100000000000000055460018103818111615536579050606052680100000000000000046040516020525f5260405f205460805260405160805167fffffffffffffffe8111615536576005015418615536575f680100000000000000046040516020525f5260405f20556060516080511015614b305760605167fffffffffffffffe8111615536576005015460a05260a05160805167fffffffffffffffe811161553657600501556080516801000000000000000460a0516020525f5260405f20555b6060516801000000000000000555565b606051614ba357601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f825f031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60a0518060ff1c6155365780670de0b6b3a764000003670de0b6b3a764000081135f83121861553657905060c0526060518060ff1c6155365760206158375f395f516362ca4b1860e05260405161010052602060e0602460fc845afa614c0b573d5f5f3e3d5ffd5b60203d106155365760e09050518060ff1c6155365760c05180820281191515600160ff1b8414151782158484840514171615615536579050905005670de0b6b3a7640000810381811361553657905060c05260805115614e3b5760206158375f395f5163b461100d61010052604051610120526040610100602461011c845afa614c97573d5f5f3e3d5ffd5b60403d106155365761010090505160e05260206158375f395f51638f8654c5610100526020610100600461011c845afa614cd3573d5f5f3e3d5ffd5b60203d106155365761010090505160e0511315614e3b5760206158375f395f516386fc88d3610160526020610160600461017c845afa614d15573d5f5f3e3d5ffd5b60203d10615536576101609050516101405260206158375f395f51632eb858e76101805260e0516101a0526020610180602461019c845afa614d59573d5f5f3e3d5ffd5b60203d10615536576101809050516101605261016051610140511115614e3b5760c05160605160206158175f395f51808202811583838304141715615536579050905061016051610140510360206158375f395f5163544fb5c1610180526040516101a0526040610180602461019c845afa614dd7573d5f5f3e3d5ffd5b60403d1061553657610180905060208101905051808202811583838304141715615536579050905060206157d75f395f518082028115838383041417156155365790509050048060ff1c615536578082018281125f831218615536579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff60405111614ea857606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b60403661a4e037617c4051604052614ec961a5206133ad565b61a520805161a4e052602081015161a5005250617c805115614f9a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff617c405160405261a4e0516060526001608052617c805160a052614f2b61a520614b40565b61a520511315614f9a57600f61a540527f4e6f7420656e6f7567682072656b74000000000000000000000000000000000061a5605261a5405061a540518061a56001601f825f031636823750506308c379a061a50052602061a52052601f19601f61a54051011660440161a51cfd5b61a4e05161a52052670de0b6b3a764000061a4e051617ca0518082028115838383041417156155365790509050670de0b6b3a763ffff81018181106155365790500461a4e05261a4e051156155365761a4e05161a520510361a5205260206158375f395f5163f3fef3a361a5a052617c405161a5c052617ca051604052617c805160605261502961a580614e43565b61a5805161a5e052604061a5a0604461a5bc5f855af161504b573d5f5f3e3d5ffd5b60403d106155365761a5a09050604061a54060408360045afa5050617c605161a5405110156150d957600861a580527f536c69707061676500000000000000000000000000000000000000000000000061a5a05261a5805061a580518061a5a001601f825f031636823750506308c379a061a54052602061a56052601f19601f61a58051011660440161a55cfd5b61a5405161a4e0518082811882841002189050905061a5805260206157f760403960206158376060393060805261a5805160a052615115613ca6565b61a5405161a4e0511161517f5760206157b760403960206158376060393360805261a5605160a052615145613ca6565b61a4e05161a5405111156153ca5760206157f760403960206158376060393360805261a4e05161a540510360a0526153ca613ca6566153ca565b61a5405161a4e0510361a5a052617cc0516151da5760206157b760403960206158376060393360805261a5605160a0526151b7613ca6565b60206157f7604039336060523060805261a5a05160a0526153ca613ca6566153ca565b60206157b76040396020615837606039617cc05160805261a5605160a052615200613ca6565b7f750abc510000000000000000000000000000000000000000000000000000000061a5c0525f61a6005261a600805160208201209050617da051617dc02018615269577f4ea696bb0000000000000000000000000000000000000000000000000000000061a5c0525b617cc05160405261a5c051606052617c405160805261a5405160a05261a5605160c05261a4e05160e052617ce05160208160051b018061010082617ce060045afa5050506020617da05101806101c082617da060045afa50506152cd61a640613d96565b61a640606061a5e060608360045afa505061a5a05161a60051101561535157601361a640527f6e6f7420656e6f7567682070726f63656564730000000000000000000000000061a6605261a6405061a640518061a66001601f825f031636823750506308c379a061a60052602061a62052601f19601f61a64051011660440161a61cfd5b61a5a05161a6005111156153865760206157f7604039617cc0516060523360805261a5a05161a600510360a052615386613ca6565b60206157f7604039617cc0516060523060805261a5a05160a0526153a8613ca6565b60206157b7604039617cc0516060523360805261a6205160a0526153ca613ca6565b680100000000000000075461a4e051808201828110615536579050905068010000000000000007556001617c40516020525f5260405f2061a52051815561a50051600182015550617c40517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5605161a5a05261a4e05161a5c052604061a5a0a2617c4051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d061a5605161a5a05261a5405161a5c05261a4e05161a5e052606061a5a0a361a520516154d957617c40517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a5a03760a061a5a0a2617c40516040526154d9614a6c565b60035461a5005180820281158383830414171561553657905090506004548015615536578082049050905061a5a05261a4e05161a5a05161a4e051808281188284110218905090500360035561a50051600455615534613320565b565b5f80fd05dd55670a02a956170800b357470703e6570708005556df050003577f0301c25657090cf855b70c00b1569f08007c98ab24050005e4c4b3960b52010536b7dbb70b1c00a524049e57090e0045bcbaf487207f00454f02c42000ef00057463129a019e0025e2d8ebee237500254ba96d4606db01055457ff7b006800259a49719602f60045371fd8e60d8500252a94394501da0005e1ec3c68008e00253d140d212f0d0045e231bff001030005b4440df40ded0065627d2b83012b0005d14ff5b6099300254189617d28d9002589fa4dd707020125720fb25406160065546e040d22450045a21adb9e024e002581d2f1b729db00250b8db6811ac7008590f8667d242200457128f3b800ad0025a757320604ac00451cf1f94703020065fadc9bfb066500859b6c56ec021f00256cce39be00db0005adddfa940a4500656f972f1209040025c45a015501cc00052c5089c326af002522c714531ad100a520849f6a099d0045d0c581bf2ef9000531dc3ca8027f0005adfae4ce011700051b1800e32c4c0005cc1891c72b8000255794e890148400a55449b9cb013f00058908ea82237f004524977ef30b2b00e52621db2f01e80005e1270b6e015300457feb639514ab00c52a0c35862aae0045765337b601f60005acb708150db4004586eba68606200085d28952fc2117010551abf14a031300851b25cdaf22330025152f65cb1475006582bfb1de04b60065d9f11a6426770005bc61ea2306cc00c580e8f6ec241100251e0cfcef2d0400051e4b77602f82002569c6804e02040005fed9698c210d00c5dd171e7c0a3b0045ec74d0a827e1002523cfed03065b0065841957978a182d185018601840184818401828184018381818190160a16576797065728300030a0029
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.