Source Code
Latest 25 from a total of 702 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Repay | 28410475 | 66 days ago | IN | 0 FRAX | 0.00001311 | ||||
| Add_collateral | 28409330 | 66 days ago | IN | 0 FRAX | 0.00001024 | ||||
| Borrow_more | 27846315 | 79 days ago | IN | 0 FRAX | 0.00003728 | ||||
| Repay | 27520353 | 86 days ago | IN | 0 FRAX | 0.00007806 | ||||
| Borrow_more | 27519859 | 86 days ago | IN | 0 FRAX | 0.00005476 | ||||
| Repay | 26742705 | 104 days ago | IN | 0 FRAX | 0.00002763 | ||||
| Borrow_more | 26311176 | 114 days ago | IN | 0 FRAX | 0.00008989 | ||||
| Borrow_more | 25408294 | 135 days ago | IN | 0 FRAX | 0.00001324 | ||||
| Borrow_more | 24904549 | 147 days ago | IN | 0 FRAX | 0.00001177 | ||||
| Add_collateral | 24865928 | 148 days ago | IN | 0 FRAX | 0.00001315 | ||||
| Create_loan | 24865836 | 148 days ago | IN | 0 FRAX | 0.00001453 | ||||
| Borrow_more | 24657884 | 153 days ago | IN | 0 FRAX | 0.00002362 | ||||
| Borrow_more | 24497816 | 156 days ago | IN | 0 FRAX | 0.00002578 | ||||
| Borrow_more | 24329691 | 160 days ago | IN | 0 FRAX | 0.00001388 | ||||
| Repay | 24271229 | 162 days ago | IN | 0 FRAX | 0.00001817 | ||||
| Create_loan | 24115773 | 165 days ago | IN | 0 FRAX | 0.0000638 | ||||
| Repay | 23862673 | 171 days ago | IN | 0 FRAX | 0.00001763 | ||||
| Add_collateral | 23761399 | 173 days ago | IN | 0 FRAX | 0.00003352 | ||||
| Repay | 23570770 | 178 days ago | IN | 0 FRAX | 0.00023083 | ||||
| Repay | 23570625 | 178 days ago | IN | 0 FRAX | 0.0002372 | ||||
| Repay | 23362675 | 183 days ago | IN | 0 FRAX | 0.00001996 | ||||
| Borrow_more | 23336805 | 183 days ago | IN | 0 FRAX | 0.00002045 | ||||
| Borrow_more | 23336695 | 183 days ago | IN | 0 FRAX | 0.00001946 | ||||
| Borrow_more | 23330924 | 183 days ago | IN | 0 FRAX | 0.00001998 | ||||
| Borrow_more | 23326126 | 183 days ago | IN | 0 FRAX | 0.00004374 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 30714652 | 12 days ago | 0 FRAX | |||||
| 30714652 | 12 days ago | 0 FRAX | |||||
| 30714652 | 12 days ago | 0 FRAX | |||||
| 30714652 | 12 days ago | 0 FRAX | |||||
| 30714652 | 12 days ago | 0 FRAX | |||||
| 30714652 | 12 days ago | 0 FRAX | |||||
| 30714652 | 12 days ago | 0 FRAX | |||||
| 30714652 | 12 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29566637 | 39 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX | |||||
| 29153430 | 49 days ago | 0 FRAX |
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x7002B727...b4dFAe9d1 The constructor portion of the code might be different and could alter the actual behaviour of the 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)))
_borrowed_token.approve(msg.sender, max_value(uint256))
@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 += loan.rate_mul
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
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):
"""
@notice Internal method to borrow and add or remove collateral
@param d_collateral Amount of collateral to add
@param d_debt Amount of debt increase
@param _for Address to transfer tokens to
@param remove_collateral Remove collateral instead of adding
"""
debt: uint256 = 0
rate_mul: uint256 = 0
debt, rate_mul = self._debt(_for)
assert debt > 0, "Loan doesn't exist"
debt += d_debt
ns: int256[2] = AMM.read_user_tick_numbers(_for)
size: uint256 = convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256)
xy: uint256[2] = AMM.withdraw(_for, 10**18)
assert xy[0] == 0, "Already in underwater mode"
if remove_collateral:
xy[1] -= d_collateral
else:
xy[1] += d_collateral
n1: int256 = self._calculate_debt_n1(xy[1], debt, size, _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)
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)
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)
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)
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)
@param _for Address to repay for
"""
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)
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 Peform a bad liquidation (or self-liquidation) of user if health is not good
@param min_x Minimal amount of stablecoin to receive (to avoid liquidators being sandwiched)
"""
discount: uint256 = 0
if user != msg.sender:
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 Peform a bad liquidation (or self-liquidation) of user if health is not good
@param min_x Minimal amount of stablecoin to receive (to avoid liquidators being sandwiched)
@param frac Fraction to liquidate; 100% = 10**18
@param callbacker Address of the callback contract
@param callback_args Extra arguments for the callback (up to 5) such as min_amount etc
"""
discount: uint256 = 0
if user != msg.sender:
discount = self.liquidation_discounts[user]
self._liquidate(user, min_x, discount, min(frac, 10**18), 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 user != msg.sender:
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
0x6157cb5150346107cb576020615e835f395f518060a01c6107cb57610160526020615ea35f395f518060a01c6107cb57610180526020615f035f395f518060a01c6107cb576101a0523361568b526101805168010000000000000008556020615ee35f395f5168010000000000000009556020615ec35f395f516801000000000000000a55670de0b6b3a76400006004556101a05161572b526101a05163f446c1d06101e05260206101e060046101fc845afa6100be573d5f5f3e3d5ffd5b60203d106107cb576101e09050516101c0526101c05161574b5260016101c0510361576b5260016101c051036101c051670de0b6b3a7640000810281670de0b6b3a76400008204186107cb5790500460a05261011b6101e061056d565b6101e05161578b5261574b51673782dace9d9000000467016345785d8a000081811867016345785d8a00008310021890506157cb52610160516101e0525f61020052610160516101f45733632621db2f610220526020610220600461023c845afa610188573d5f5f3e3d5ffd5b60203d106107cb57610220518060a01c6107cb57610260526102609050516101e0523363765337b6610220526020610220600461023c845afa6101cd573d5f5f3e3d5ffd5b60203d106107cb57610220518060a01c6107cb57610260526102609050516102005261023a565b3363e9cbd822610220526020610220600461023c845afa610217573d5f5f3e3d5ffd5b60203d106107cb57610220518060a01c6107cb5761026052610260905051610200525b6101e0516156ab52610200516156eb526101e05163313ce567610220526020610220600461023c845afa610270573d5f5f3e3d5ffd5b60203d106107cb5761022090505180601203601281116107cb579050600a0a6156cb526102005163313ce567610220526020610220600461023c845afa6102b9573d5f5f3e3d5ffd5b60203d106107cb5761022090505180601203601281116107cb579050600a0a61570b5260016101c051036101c0516ec097ce7bc90715b34b9f10000000008102816ec097ce7bc90715b34b9f10000000008204186107cb579050048060b571010000000000000000000000000000000000821061033d578160801c91508060401b90505b6901000000000000000000821061035b578160401c91508060201b90505b650100000000008210610375578160201c91508060101b90505b6301000000821061038d578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090506157ab526102005163095ea7b36102205233610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610260526020610220604461023c5f855af1610449573d5f5f3e3d5ffd5b60203d106107cb57610220518060011c6107cb5761028052610280505061568b6107cf610000396157eb610000f35b6040516060525f60805260405160801c1561049c5760405160801c60605260806080525b60605160401c156104ba5760605160401c6060526040608051016080525b60605160201c156104d85760605160201c6060526020608051016080525b60605160101c156104f65760605160101c6060526010608051016080525b60605160081c156105145760605160081c6060526008608051016080525b60605160041c156105325760605160041c6060526004608051016080525b60605160021c156105505760605160021c6060526002608051016080525b60605160011c15610565576001608051016080525b608051815250565b60a0518060ff1c6107cb5760c05260a051156107cb57606060a051604052610596610100610478565b610100518060ff1c6107cb570360e05260c05160e051609f035f81126107cb571b609f1c8060ff1c6107cb5760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b5f80fd5f3560e01c60056005600983060261542e01601b395f5160088160ff16838360181c0260181c0660031b8260081c61ffff1601601839505f51818160201c14600336111661004c57612f71565b8061fffe1636103482600116021761542a578060101c61ffff16565b6004358060a01c61542a5760405260026040516020525f5260405f205460605260206060f35b60043567fffffffffffffffe811161542a576005015460405260206040f35b6004358060a01c61542a57604052680100000000000000046040516020525f5260405f205460605260206060f35b680100000000000000055460405260206040f35b680100000000000000065460405260206040f35b680100000000000000075460405260206040f35b680100000000000000085460405260206040f35b680100000000000000095460405260206040f35b6801000000000000000a5460405260206040f35b6004358060a01c61542a576040526024358060a01c61542a576060526801000000000000000b6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6004358060a01c61542a576040526801000000000000000c6040516020525f5260405f205460605260206060f35b602061568b60403960206040f35b602061572b60403960206040f35b60206156ab60403960206040f35b60206156eb60403960206040f35b5f5460021461542a5760025f556102196132c8565b60035f55005b6004358060a01c61542a5760e0525f5460021461542a57602060e051604052610249610100613355565b610100f35b6004358060a01c61542a576040525f5460021461542a5760016040516020525f5260405f2054151560605260206060f35b602061572b5f395f5163095a0fc6606052602060606004607c845afa6102a7573d5f5f3e3d5ffd5b60203d1061542a57606090505160405260035460605260045460805260605160405180820281158383830414171561542a5790509050608051801561542a578082049050905060a052602060a0f35b6040366102203761032a565b604435610220525f6102405261032a565b604435610220526064358060a01c61542a57610240525b5f5460021461542a5760046024351015610344575f61034c565b603260243511155b1561542a5760043560206156cb5f395f5180820281158383830414171561542a57905090506040526024356060526801000000000000000a546801000000000000000c610240516020525f5260405f205480820182811061542a57905090506080526103b961028061343f565b61028051610260526001670de0b6b3a7640000610260516103db6102a06139d2565b6102a05180820281158383830414171561542a579050905004600181811860018311021890500361028052602061570b5f395f51670de0b6b3a76400000261028051670de05bc096e9c000810281670de05bc096e9c00082041861542a57905004610280526102805160206156eb5f395f516370a082316102a052306102c05260206102a060246102bc845afa610474573d5f5f3e3d5ffd5b60203d1061542a576102a09050516102205180820182811061542a5790509050808281188284100218905090506102e05260206102e0f35b5f610220526104c6565b6044358060a01c61542a57610220525b5f5460021461542a5760326024351161542a57670de05bc096e9c00060206156cb5f395f51600435602061570b5f395f51670de0b6b3a76400000280820281158383830414171561542a57905090506105206102406139d2565b61024051801561542a5780820490509050670de0b6b3a7640000810281670de0b6b3a764000082041861542a579050670de0b6b3a76400006040526024356060526801000000000000000a546801000000000000000c610220516020525f5260405f205480820182811061542a57905090506080526105a061026061343f565b61026051801561542a5780820490509050600160206156cb5f395f51036107d060243501602435020180820182811061542a579050905004670de0b6b3a7640000810281670de0b6b3a764000082041861542a57905004610280526020610280f35b5f6103205261061c565b6064358060a01c61542a57610320525b5f5460021461542a5760206060600461016037610320516101c05261064261034061352c565b610340f35b3361050052610661565b6064358060a01c61542a57610500525b5f5460021461542a5760025f55326105005114610694576105005160405261068a6105206144e0565b610520511561542a575b6060600461032037600161038052610500516103a0526106b26140ad565b60035f55005b5f617d20523361a4605261071b565b60a43560040161271081351161542a5760208135018082617d203750503361a4605261071b565b60a43560040161271081351161542a5760208135018082617d2037505060c4358060a01c61542a5761a460525b6064358060a01c61542a57617c4052608435600401600581351161542a57803560208160051b018083617c60375050505f5460021461542a5760025f553261a460511461077e5761a4605160405261077461a4a06144e0565b61a4a0511561542a575b60206156eb604039617c405160605260243560805261079b613cc7565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610804577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa505061085e61a4e0613d3b565b61a4e06040810190505161a4c05260043561a4c05180820182811061542a57905090506103205260406024610340375f6103805261a460516103a0526108a26140ad565b60206156ab60403933606052602061572b60803960043560a0526108c4613c4b565b60206156ab604039617c4051606052602061572b60803961a4c05160a0526108ea613c4b565b60035f55005b336105805261090a565b6024358060a01c61542a57610580525b5f5460021461542a5760025f556004356109235761096e565b600435610320525f6103405261058051610360525f6103805261094461451a565b60206156ab60403933606052602061572b60803960043560a052610966613c4b565b61096e6132c8565b60035f55005b336105805261098e565b6024358060a01c61542a57610580525b5f5460021461542a5760025f556004356109a757610a11565b610580516040526109b96105a06144e0565b6105a0511561542a57600435610320525f6103405261058051610360526001610380526109e461451a565b60206156ab604039602061572b6060396105805160805260043560a052610a09613c4b565b610a116132c8565b60035f55005b3361058052610a31565b6044358060a01c61542a57610580525b5f5460021461542a5760025f55602435610a4a57610af0565b61058051604052610a5c6105a06144e0565b6105a0511561542a57604060046103203761058051610360525f61038052610a8261451a565b680100000000000000065460243580820182811061542a5790509050680100000000000000065560206156ab60403933606052602061572b60803960043560a052610acb613c4b565b60206156eb60403961058051606052602435608052610ae8613cc7565b610af06132c8565b60035f55005b5f617d20523361a46052610b59565b60843560040161271081351161542a5760208135018082617d203750503361a46052610b59565b60843560040161271081351161542a5760208135018082617d2037505060a4358060a01c61542a5761a460525b6044358060a01c61542a57617c4052606435600401600581351161542a57803560208160051b018083617c60375050505f5460021461542a5760025f55602435610ba257610d57565b61a46051604052610bb461a4a06144e0565b61a4a0511561542a5760206156eb604039617c4051606052602435608052610bda613cc7565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610c43577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa5050610c9d61a4e0613d3b565b61a4e06040810190505161a4c05260043561a4c05180820182811061542a5790509050610320526024356103405261a46051610360525f61038052610ce061451a565b680100000000000000065460243580820182811061542a5790509050680100000000000000065560206156ab60403933606052602061572b60803960043560a052610d29613c4b565b60206156ab604039617c4051606052602061572b60803961a4c05160a052610d4f613c4b565b610d576132c8565b60035f55005b33610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610ddc565b6024358060a01c61542a57610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610ddc565b6024358060a01c61542a5761032052604435610340525b5f5460021461542a5760025f55600435610df557611447565b6040366103603761032051604052610e0e6103a0613355565b6103a08051610360526020810151610380525061036051610e8e5760126103a0527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103c0526103a0506103a051806103c001601f825f031636823750506308c379a061036052602061038052601f19601f6103a051011660440161037cfd5b61036051600435808281188284100218905090506103a0526103a05161036051036103605261032051604052610ec56103e06144e0565b6103e0516103c0526103605161101857602061572b5f395f5163f3fef3a3610420526103205161044052670de0b6b3a7640000610460526040610420604461043c5f855af1610f16573d5f5f3e3d5ffd5b60403d1061542a57610420905060406103e060408360045afa50506103e05115610f69576103c0511561542a5760206156eb604039602061572b606039610320516080526103e05160a052610f69613c4b565b6104005115610f985760206156ab604039602061572b606039610320516080526104005160a052610f98613c4b565b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366104203760a0610420a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061040051610420526103a051610440526040610420a26103205160405261138661497456611386565b602061572b5f395f5163c16ef264610400526020610400600461041c845afa611043573d5f5f3e3d5ffd5b60203d1061542a576104009050516103e052610340516103e0511361542a57602061572b5f395f5163b461100d6104405261032051610460526040610440602461045c845afa611095573d5f5f3e3d5ffd5b60403d1061542a576104409050604061040060408360045afa5050610400516104205103610440526002610320516020525f5260405f2054610460526103e051610400511361118b57610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61048052610360516104a052610400516104c052610420516104e052610460516105005260a0610480a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610480526103a0516104a0526040610480a261134e565b602061572b5f395f5163f3fef3a36104c052610320516104e052670de0b6b3a76400006105005260406104c060446104dc5f855af16111cc573d5f5f3e3d5ffd5b60403d1061542a576104c09050604061048060408360045afa50506104a051610160526103605161018052600161044051015f811261542a576101a052610320516101c05261121c6104e061352c565b6104e0516104c0526104c051610440518082018281125f83121861542a57905090506104e052602061572b5f395f5163ab047e006105005261032051610520526104a051610540526104c051610560526104e05161058052803b1561542a575f610500608461051c5f855af1611294573d5f5f3e3d5ffd5b506103c051156112c257680100000000000000095461046052610460516002610320516020525f5260405f20555b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26104a0516105005261036051610520526104c051610540526104e05161056052610460516105805260a0610500a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610500526103a051610520526040610500a25b6103c05161138657600161032051604052610360516060525f6080526104605160a05261137c610480614a48565b610480511261542a575b60206156eb60403933606052306080526103a05160a0526113a5613c4b565b68010000000000000007546103a05180820182811061542a579050905068010000000000000007556001610320516020525f5260405f20610360518155610380516001820155506003546103805180820281158383830414171561542a5790509050600454801561542a57808204905090506103e0526103a0516103e0516103a0518082811882841102189050905003600355610380516004556114476132c8565b60035f55005b5f617d20523361a460526114b0565b60443560040161271081351161542a5760208135018082617d203750503361a460526114b0565b60443560040161271081351161542a5760208135018082617d203750506064358060a01c61542a5761a460525b6004358060a01c61542a57617c4052602435600401600581351161542a57803560208160051b018083617c60375050505f5460021461542a5760025f5561a460516040526114ff61a4a06144e0565b61a4a0511561542a57602061572b5f395f5163b461100d61a4e05261a4605161a50052604061a4e0602461a4fc845afa61153b573d5f5f3e3d5ffd5b60403d1061542a5761a4e09050604061a4a060408360045afa5050602061572b5f395f5163f3fef3a361a5205261a4605161a54052670de0b6b3a764000061a56052604061a520604461a53c5f855af1611597573d5f5f3e3d5ffd5b60403d1061542a5761a5209050604061a4e060408360045afa505060403661a5203761a460516040526115cb61a560613355565b61a560805161a52052602081015161a540525060206156ab604039602061572b606039617c405160805261a5005160a052611604613c4b565b7e8ae1880000000000000000000000000000000000000000000000000000000061a560525f61a5a05261a5a0805160208201209050617d2051617d40201861166c577fef67dc740000000000000000000000000000000000000000000000000000000061a560525b617c405160405261a5605160605261a4605160805261a4e05160a05261a5005160c05261a5205160e052617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506116d061a5e0613d3b565b61a5e0606061a58060608360045afa505061a5a05161a4e05180820182811061542a579050905061a5e05261a5e0511561542a575f61a6005261a5205161a5e05110156118d25761a4a05161a4c0510361a6205261a5805161a4a051131561542a5761a5a05161a6005261a5a05161a520510361a5205261a5c0516101605261a5205161018052600161a62051015f811261542a576101a05261a460516101c05261177c61a66061352c565b61a6605161a6405261a6405161a620518082018281125f83121861542a579050905061a66052602061572b5f395f5163ab047e0061a6805261a4605161a6a05261a5c05161a6c05261a6405161a6e05261a6605161a70052803b1561542a575f61a680608461a69c5f855af16117f4573d5f5f3e3d5ffd5b50680100000000000000095461a6805261a68051600261a460516020525f5260405f205560206156ab604039617c4051606052602061572b60803961a5c05160a05261183e613c4b565b60206156eb604039617c40516060523060805261a5a05160a052611860613c4b565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261a5c05161a6a05261a5205161a6c05261a6405161a6e05261a6605161a7005261a6805161a7205260a061a6a0a261a5005161a5c05180820382811161542a579050905061a500526119bc565b61a5205161a600525f61a5205261a460516040526118ee614974565b60206156eb604039617c40516060523060805261a5a05160a052611910613c4b565b60206156eb604039602061572b6060393060805261a4e05160a052611933613c4b565b61a6005161a5e05111156119645760206156eb60403961a4605160605261a6005161a5e05103608052611964613cc7565b60206156ab604039617c405160605261a4605160805261a5c05160a052611989613c4b565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a6203760a061a620a25b61a460517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5005161a6205261a6005161a64052604061a620a2680100000000000000075461a6005180820182811061542a57905090506801000000000000000755600161a460516020525f5260405f2061a52051815561a5405160018201555060035461a5405180820281158383830414171561542a5790509050600454801561542a578082049050905061a6205261a6005161a6205161a60051808281188284110218905090500360035561a54051600455611a996132c8565b60035f55005b5f61036052611ab1565b608435610360525b6004358060a01c61542a57610320526064358060011c61542a57610340525f5460021461542a57602061572b5f395f5163b461100d6103c052610320516103e05260406103c060246103dc845afa611b0b573d5f5f3e3d5ffd5b60403d1061542a576103c09050604061038060408360045afa505061032051604052611b386103e0613355565b6103e0518060ff1c61542a576103c052610360516103e0525f610400526103c05115611b96576002610320516020525f5260405f20548060ff1c61542a57610400526001610380516103a05103015f811261542a576103e052611bd3565b68010000000000000009548060ff1c61542a57610400527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610380525b606036610420376103c0516044358082018281125f83121861542a57905090506103c05260016103c0511215611c68576011610480527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104a0526104805061048051806104a001601f825f031636823750506308c379a061044052602061046052601f19601f61048051011660440161045cfd5b602061572b5f395f5163c16ef2646104a05260206104a060046104bc845afa611c93573d5f5f3e3d5ffd5b60203d1061542a576104a090505161048052610480516103805113611d33576103805161042052602061572b5f395f516362ca4b186104a052610320516104c05260206104a060246104bc845afa611ced573d5f5f3e3d5ffd5b60203d1061542a576104a0905051602061570b5f395f51670de0b6b3a76400000280820281158383830414171561542a57905090508060ff1c61542a5761046052611e18565b602061572b5f395f5163544fb5c16104a052610320516104c05260406104a060246104bc845afa611d66573d5f5f3e3d5ffd5b60403d1061542a576104a09050602081019050518060ff1c61542a576024358082018281125f83121861542a579050905061044052610440515f811261542a57610160526103c0515f811261542a57610180526103e0516101a052610320516101c052611dd46104a061352c565b6104a051610420526104405160206156cb5f395f518060ff1c61542a5780820281191515600160ff1b841415178215848484051417161561542a5790509050610440525b6103c051602061570b5f395f518060ff1c61542a5780820281191515600160ff1b841415178215848484051417161561542a57905090506103c052602061572b5f395f51632eb858e76104c052610420516104e05260206104c060246104dc845afa611e86573d5f5f3e3d5ffd5b60203d1061542a576104c09050518060ff1c61542a576104a05261048051610380511315611f0957610440515f811261542a576040526103e0516060525f608052611ed26104c061343f565b6104c0518060ff1c61542a576104a05180820281191515600160ff1b841415178215848484051417161561542a5790509050610460525b6103c05161046051056104c0526104c051670de0b6b3a76400006104c0516104005180820281191515600160ff1b841415178215848484051417161561542a5790509050058082038281135f83121861542a5790509050670de0b6b3a7640000810381811361542a5790506104c05261034051156120505761048051610420511315612050576104a051602061572b5f395f516386fc88d3610500526020610500600461051c845afa611fbe573d5f5f3e3d5ffd5b60203d1061542a576105009050518060ff1c61542a57808281188284130218905090506104a0518082038281135f83121861542a57905090506104e05260016104e05112612050576104c0516103c0516104e0516104405180820281191515600160ff1b841415178215848484051417161561542a5790509050058082018281125f83121861542a57905090506104c0525b60206104c0f35b6004358060a01c61542a5761a6a0525f5460021461542a5760025f555f61a6c0523361a6a0511461209757600261a6a0516020525f5260405f205461a6c0525b61a6a051617c4052602435617c605261a6c051617c8052670de0b6b3a7640000617ca052604036617cc0375f617da0526120cf614db8565b60035f55005b5f61a7a0526120fd565b60a43560040161271081351161542a576020813501808261a7a03750505b6004358060a01c61542a5761a6a0526064358060a01c61542a5761a6c052608435600401600581351161542a57803560208160051b01808361a6e0375050505f5460021461542a5760025f555f61cf00523361a6a0511461216d57600261a6a0516020525f5260405f205461cf00525b61a6a051617c4052602435617c605261cf0051617c8052604435670de0b6b3a7640000818118670de0b6b3a7640000831002189050617ca05261a6c051617cc05261a6e05160208160051b0180617ce08261a6e060045afa505050602061a7a0510180617da08261a7a060045afa50506121e5614db8565b60035f55005b670de0b6b3a764000061010052612205565b602435610100525b6004358060a01c61542a5760e0525f5460021461542a575f610120523360e0511461223e57600260e0516020525f5260405f2054610120525b670de0b6b3a7640000602061572b5f395f5163544fb5c16101605260e051610180526040610160602461017c845afa612279573d5f5f3e3d5ffd5b60403d1061542a5761016090505161010051604052610120516060526122a06101c0614d4b565b6101c05180820281158383830414171561542a57905090500461014052670de0b6b3a764000060e0516040526122d7610180613355565b610180516101005180820281158383830414171561542a579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f35b5f61020052612337565b6024358060011c61542a57610200525b6004358060a01c61542a576101e0525f5460021461542a5760206101e051610280526101e05160405261236b610220613355565b610220516102a052610200516102c05260026101e0516020525f5260405f20546102e05260806040608061028060045afa506123a8610260614a48565b610260f35b6040366101e0376123d3565b6004356101e0525f610200526123d3565b604060046101e0375b5f5460021461542a576801000000000000000554610220526102005161024052610200516124045761022051610240525b6101e051610260525f610280525f620f4240905b80620273a0526102205161026051101561243c5761024051620273a051181561243f565b60015b15612449576125b1565b6102605167fffffffffffffffe811161542a5760050154620273c052620273c05160405261247962027400613355565b6202740051620273e052620273c051604052620273e05160605260016080526002620273c0516020525f5260405f205460a0526124b862027420614a48565b620274205162027400527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff62027400511361259157602061572b5f395f5163544fb5c16202746052620273c051620274805260406202746060246202747c845afa612525573d5f5f3e3d5ffd5b60403d1061542a5762027460905060406202742060408360045afa5050610280516103e7811161542a5760a081026102a001620273c05181526202742051602082015262027440516040820152620273e051606082015262027400516080820152506001810161028052505b610260516001810181811061542a57905061026052600101818118612418575b5050602080620273a05280620273a0015f6102805180835260a081025f826103e8811161542a57801561260957905b60a081026102a00160a08202602088010160a08160a08460045afa5050506001018181186125e0575b50508201602001915050905081019050620273a0f35b6020602061572b5f395f5163f2388acb604052602060406004605c845afa612649573d5f5f3e3d5ffd5b60203d1061542a5760409050f35b6004358060a01c61542a576040525f5460021461542a57602061572b5f395f5163e8dd1ef1606052604051608052602060606024607c845afa61269c573d5f5f3e3d5ffd5b60203d1061542a576060518060011c61542a5760a05260a09050511561542a57602061572b5f395f5163b461100d60a05260405160c052604060a0602460bc845afa6126ea573d5f5f3e3d5ffd5b60403d1061542a5760a090506040606060408360045afa5050602061572b5f395f51632eb858e760a05260605160c052602060a0602460bc845afa612731573d5f5f3e3d5ffd5b60203d1061542a5760a090505161012052602061572b5f395f516324299b7a60e05260805161010052602060e0602460fc845afa612771573d5f5f3e3d5ffd5b60203d1061542a5760e0905051610140526040610120f35b6004358060a01c61542a5760e0525f5460021461542a57602061572b5f395f5163544fb5c16101405260e051610160526040610140602461015c845afa6127d2573d5f5f3e3d5ffd5b60403d1061542a576101409050604061010060408360045afa5050602061572b5f395f5163b461100d6101805260e0516101a0526040610180602461019c845afa61281f573d5f5f3e3d5ffd5b60403d1061542a576101809050604061014060408360045afa5050610120516101c052610100516101e05260e05160405261285b610180613355565b61018051610200526001610140516101605103015f811261542a576102205260806101c0f35b602061568b5f395f5163f851a440604052602060406004605c845afa6128a9573d5f5f3e3d5ffd5b60203d1061542a576040518060a01c61542a576080526080905051331861542a5760206157cb5f395f5160043511156128e2575f6128ec565b620f424060043510155b61294b5760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b602061572b5f395f51631aa02d59604052600435606052803b1561542a575f60406024605c5f855af1612980573d5f5f3e3d5ffd5b50005b6004358060a01c61542a576040525f5460021461542a5760025f55602061568b5f395f5163f851a440606052602060606004607c845afa6129c6573d5f5f3e3d5ffd5b60203d1061542a576060518060a01c61542a5760a05260a0905051331861542a57604051680100000000000000085560405163e91f2f4c606052602060606004607c5f855af1612a18573d5f5f3e3d5ffd5b60203d1061542a57606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a160035f55005b5f5460021461542a5760025f55602061568b5f395f5163f851a440604052602060406004605c845afa612a8b573d5f5f3e3d5ffd5b60203d1061542a576040518060a01c61542a576080526080905051331861542a57602435600435111561542a57662386f26fc100006024351061542a576706f05b59d3b200006004351161542a5760243568010000000000000009556004356801000000000000000a557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a160035f55005b6004358060a01c61542a576040525f5460021461542a5760025f55602061568b5f395f5163f851a440606052602060606004607c845afa612b6b573d5f5f3e3d5ffd5b60203d1061542a576060518060a01c61542a5760a05260a0905051331861542a57602061572b5f395f5163cc1891c7606052604051608052803b1561542a575f60606024607c5f855af1612bc1573d5f5f3e3d5ffd5b507fc770618faa286dffef5f2a2fea0c0685d5e0d18110242589ab70e5ae36f94ab560405160605260206060a160035f55005b602061572b5f395f5163095a0fc6606052602060606004607c845afa612c1c573d5f5f3e3d5ffd5b60203d1061542a57606090505160405260035460605260045460805260605160405180820281158383830414171561542a5790509050608051801561542a5780820490509050680100000000000000075480820182811061542a5790509050606052680100000000000000065460a05260a05160605160a051808281188284110218905090500360c052602060c0f35b5f5460021461542a5760025f55602061568b5f395f5163cab4d3db610140526020610140600461015c845afa612ce4573d5f5f3e3d5ffd5b60203d1061542a57610140518060a01c61542a576101805261018090505161012052602061572b5f395f5163095a0fc6610160526020610160600461017c845afa612d31573d5f5f3e3d5ffd5b60203d1061542a57610160905051610140526003546101605260045461018052610160516101405180820281158383830414171561542a579050905061018051801561542a57808204905090506101605261014051610180526101605160035561018051600455612da06132c8565b61016051680100000000000000075480820182811061542a57905090506101a05268010000000000000006546101c0526101c0516101a05111612e24577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f5f6101e052610160516102005260406101e0a15f6101e05260206101e0612e9b56612e9b565b6101a05168010000000000000006556101c0516101a051036101a05260206156eb604039610120516060526101a051608052612e5e613cc7565b7f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f6101a0516101e052610160516102005260406101e0a160206101a05b60035f55f35b5f5460021461542a57600160405260206040f35b6004358060a01c61542a576040526024358060011c61542a576060526060516801000000000000000b336020525f5260405f20806040516020525f5260405f20905055604051337f1d3e246ebbc933bf65d3290db9f93d67ab91a12d2b19308a35806e04d1c174c560605160805260206080a3005b6004356801000000000000000c336020525f5260405f2055337f06159bbd6e0c9c85edd120aebed689bc1c6bd79decfedbdf2f8c17abf681ca3c60043560405260206040a2005b5f5ffd5b6040516060525f60805260405160801c15612f995760405160801c60605260806080525b60605160401c15612fb75760605160401c6060526040608051016080525b60605160201c15612fd55760605160201c6060526020608051016080525b60605160101c15612ff35760605160101c6060526010608051016080525b60605160081c156130115760605160081c6060526008608051016080525b60605160041c1561302f5760605160041c6060526004608051016080525b60605160021c1561304d5760605160021c6060526002608051016080525b60605160011c15613062576001608051016080525b608051815250565b60a0518060ff1c61542a5760c05260a0511561542a57606060a051604052613093610100612f75565b610100518060ff1c61542a570360e05260c05160e051609f035f811261542a571b609f1c8060ff1c61542a5760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b680100000000000000085463e91f2f4c606052602060606004607c5f855af16132f3573d5f5f3e3d5ffd5b60203d1061542a576060905051640a3c2abcef818118640a3c2abcef831002189050604052602061572b5f395f5163d4387a99606052604051608052602060606024607c5f855af1613347573d5f5f3e3d5ffd5b60203d1061542a5760605050565b602061572b5f395f5163095a0fc6608052602060806004609c845afa61337d573d5f5f3e3d5ffd5b60203d1061542a57608090505160605260016040516020525f5260405f208054608052600181015460a052506080516133c5575f815260605160208201525061343d5661343d565b60805160605180820281158383830414171561542a579050905060c05260c05160a051801561542a5780820690509050156134245760026801000000000000000554106134245760c05160a05180820182811061542a579050905060c0525b60a05160c0510460c05260c05181526060516020820152505b565b60605160206157ab5f395f5102604051608051606051604051046103e88181186103e8831102189050683635c9adc5dea000000480820182811061542a5790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a76400000380820281158383830414171561542a57905090500460a05260a05160c052600160318101905b8060e05260605160e051186134e057613522565b602061574b5f395f5160a051602061576b5f395f5180820281158383830414171561542a57905090500460a05260a05160c0510160c0526001018181186134cc575b505060c051815250565b610180516135995760076101e0527f4e6f206c6f616e00000000000000000000000000000000000000000000000000610200526101e0506101e0518061020001601f825f031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b602061572b5f395f51638f8654c5610200526020610200600461021c845afa6135c4573d5f5f3e3d5ffd5b60203d1061542a576102009050516101e052602061572b5f395f51632eb858e7610220526101e051610240526020610220602461023c845afa613609573d5f5f3e3d5ffd5b60203d1061542a57610220905051610200526101605160206156cb5f395f5180820281158383830414171561542a57905090506040526101a0516060526801000000000000000a546801000000000000000c6101c0516020525f5260405f205480820182811061542a579050905060805261368561024061343f565b610240516102205261018051602061570b5f395f5180820281158383830414171561542a57905090506001810181811061542a579050610220516102005180820281158383830414171561542a579050905004610220526102205161374957600e610240527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006102605261024050610240518061026001601f825f031636823750506308c379a061020052602061022052601f19601f61024051011660440161021cfd5b6102205160a05261375b61026061306a565b61026051610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61024051136137b457610240516001602061578b5f395f51038082038281135f83121861542a5790509050610240525b602061578b5f395f51610240510561024052610240516101a0518060ff1c61542a57806104000361040081135f83121861542a579050808281188284120218905090506101e0518082018281125f83121861542a5790509050610240526101e05161024051136138e557602061572b5f395f5163ec65470661026052610240516001810381811361542a579050610280526020610260602461027c845afa61385e573d5f5f3e3d5ffd5b60203d1061542a57610260518060011c61542a576102a0526102a09050516138e557600d6102c0527f4465627420746f6f2068696768000000000000000000000000000000000000006102e0526102c0506102c051806102e001601f825f031636823750506308c379a06102805260206102a052601f19601f6102c051011660440161029cfd5b602061572b5f395f516386fc88d36102a05260206102a060046102bc845afa613910573d5f5f3e3d5ffd5b60203d1061542a576102a0905051602061572b5f395f51632eb858e76102605261024051610280526020610260602461027c845afa613951573d5f5f3e3d5ffd5b60203d1061542a57610260905051106139c957600d6102e0527f4465627420746f6f206869676800000000000000000000000000000000000000610300526102e0506102e0518061030001601f825f031636823750506308c379a06102a05260206102c052601f19601f6102e05101166044016102bcfd5b61024051815250565b602061572b5f395f516386fc88d3610180526020610180600461019c845afa6139fd573d5f5f3e3d5ffd5b60203d1061542a5761018090505161016052602061572b5f395f5163a7db79a56101a05260206101a060046101bc845afa613a3a573d5f5f3e3d5ffd5b60203d1061542a576101a0905051670de0b6b3a7640000810281670de0b6b3a764000082041861542a57905061016051801561542a578082049050905060a052613a856101e061306a565b6101e051610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113613ae85761018051602061578b5f395f516001810381811361542a5790508082038281135f83121861542a5790509050610180525b602061578b5f395f5161018051056005810181811261542a57905061018052602061572b5f395f5163c16ef2646101c05260206101c060046101dc845afa613b32573d5f5f3e3d5ffd5b60203d1061542a576101c09050516101a052610180516101a0516001810181811261542a5790508082811882841302189050905061018052602061572b5f395f51632eb858e76101e052610180516102005260206101e060246101fc845afa613b9d573d5f5f3e3d5ffd5b60203d1061542a576101e09050516101c0525f610401905b806101e052610180516001810381811361542a579050610180526101a0516101805113613be157613c3f565b6101c05161020052602061576b5f395f516101c051602061574b5f395f5180820281158383830414171561542a5790509050046101c052610160516101c0511115613c3457610200518352505050613c49565b600101818118613bb5575b50506101c0518152505b565b60a05115613cc5576040516323b872dd60c05260605160e0526080516101005260a05161012052602060c0606460dc5f855af1613c8a573d5f5f3e3d5ffd5b3d613ca157803b1561542a57600161014052613cb9565b60203d1061542a5760c0518060011c61542a57610140525b6101409050511561542a575b565b60805115613d395760405163a9059cbb60a05260605160c05260805160e052602060a0604460bc5f855af1613cfe573d5f5f3e3d5ffd5b3d613d1557803b1561542a57600161010052613d2d565b60203d1061542a5760a0518060011c61542a57610100525b6101009050511561542a575b565b60206156ab5f395f516040511461542a5760206156eb5f395f516040511461542a5760603661290037602061572b5f395f51638f8654c5612960526020612960600461297c845afa613d8f573d5f5f3e3d5ffd5b60203d1061542a5761296090505161290052602061572b5f395f5163ebcb006761298052612900516129a0526020612980602461299c845afa613dd4573d5f5f3e3d5ffd5b60203d1061542a5761298090505161296052602061572b5f395f516331f7e3066129a052612900516129c05260206129a060246129bc845afa613e19573d5f5f3e3d5ffd5b60203d1061542a576129a0905051612980526040515a5f60605181615300015260048101905060c0608051612a205260a051612a405260c051612a605260e051612a805280612aa05280612a20015f610100518083528060051b5f826005811161542a578015613ea357905b8060051b61012001518160051b602088010152600101818118613e85575b5050820160200191505090508101905080612ac05280612a200160206101c051018082826101c060045afa50508051806020830101601f825f03163682375050601f19601f82516020010116905081019050612a0052612a0080516020820183615300018281848460045afa50505080830192505050806152e0526152e050506040617c006152e0516153005f8686f190509050613f43573d5f5f3e3d5ffd5b3d604081183d6040100218617be052617be06020815101806129a0828460045afa5050506129a05160201161542a576129c051612a20526020612a0052612a006020810151815160200360031b1c9050612920526129a05160401161542a576129e051612a20526020612a0052612a006020810151815160200360031b1c905061294052602061572b5f395f51638f8654c5612a00526020612a006004612a1c845afa613ff2573d5f5f3e3d5ffd5b60203d1061542a57612a00905051612900511861542a57602061572b5f395f5163ebcb0067612a005261290051612a20526020612a006024612a1c845afa61403c573d5f5f3e3d5ffd5b60203d1061542a57612a00905051612960511861542a57602061572b5f395f516331f7e306612a005261290051612a20526020612a006024612a1c845afa614086573d5f5f3e3d5ffd5b60203d1061542a57612a00905051612980511861542a57606081606061290060045afa5050565b60016103a0516020525f5260405f2054156141275760146103c0527f4c6f616e20616c726561647920637265617465640000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b600461036051101561419857600f6103c0527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b603261036051111561420957600f6103c0527f4e656564206c657373207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b61032051610160526103405161018052610360516101a0526103a0516101c0526142346103e061352c565b6103e0516103c0526103c051600161036051038060ff1c61542a578082018281125f83121861542a57905090506103e052602061572b5f395f5163095a0fc6610420526020610420600461043c845afa614290573d5f5f3e3d5ffd5b60203d1061542a576104209050516104005260016103a0516020525f5260405f20610340518155610400516001820155506801000000000000000954610420526104205160026103a0516020525f5260405f20556801000000000000000554610440526103a0516104405167fffffffffffffffe811161542a576005015561044051680100000000000000046103a0516020525f5260405f20556001610440510168010000000000000005556003546104005180820281158383830414171561542a5790509050600454801561542a57808204905090506103405180820182811061542a579050905060035561040051600455602061572b5f395f5163ab047e00610460526103a05161048052610320516104a0526103c0516104c0526103e0516104e052803b1561542a575f610460608461047c5f855af16143d5573d5f5f3e3d5ffd5b5068010000000000000006546103405180820182811061542a5790509050680100000000000000065561038051156144485760206156ab60403933606052602061572b6080396103205160a05261442a613c4b565b60206156eb6040396103a05160605261034051608052614448613cc7565b6144506132c8565b6103a0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610320516104605261034051610480526103c0516104a0526103e0516104c052610420516104e05260a0610460a26103a0517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516104605261034051610480526040610460a2565b60405133186144f0576001614515565b6801000000000000000b6040516020525f5260405f2080336020525f5260405f209050545b815250565b6040366103a037610360516040526145336103e0613355565b6103e080516103a05260208101516103c052506103a0516145b35760126103e0527f4c6f616e20646f65736e27742065786973740000000000000000000000000000610400526103e0506103e0518061040001601f825f031636823750506308c379a06103a05260206103c052601f19601f6103e05101166044016103bcfd5b6103a0516103405180820182811061542a57905090506103a052602061572b5f395f5163b461100d6104205261036051610440526040610420602461043c845afa614600573d5f5f3e3d5ffd5b60403d1061542a57610420905060406103e060408360045afa505060016103e0516104005103015f811261542a5761042052602061572b5f395f5163f3fef3a361048052610360516104a052670de0b6b3a76400006104c0526040610480604461049c5f855af1614673573d5f5f3e3d5ffd5b60403d1061542a576104809050604061044060408360045afa505061044051156146fc57601a610480527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104a0526104805061048051806104a001601f825f031636823750506308c379a061044052602061046052601f19601f61048051011660440161045cfd5b6103805161472357610460516103205180820182811061542a57905090506104605261473e565b610460516103205180820382811161542a5790509050610460525b61046051610160526103a05161018052610420516101a052610360516101c0526147696104a061352c565b6104a05161048052610480516103e05161040051038082018281125f83121861542a57905090506104a052602061572b5f395f5163ab047e006104c052610360516104e052610460516105005261048051610520526104a05161054052803b1561542a575f6104c060846104dc5f855af16147e6573d5f5f3e3d5ffd5b506001610360516020525f5260405f206103a05181556103c0516001820155505f6104c05233610360511861483d5768010000000000000009546104c0526104c0516002610360516020525f5260405f2055614852565b6002610360516020525f5260405f20546104c0525b61034051156148a3576003546103c05180820281158383830414171561542a5790509050600454801561542a57808204905090506103405180820182811061542a57905090506003556103c0516004555b610380516148eb57610360517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516104e052610340516105005260406104e0a261491f565b610360517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb54610320516104e05260206104e0a25b610360517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610460516104e0526103a0516105005261048051610520526104a051610540526104c0516105605260a06104e0a2565b68010000000000000005546001810381811161542a579050606052680100000000000000046040516020525f5260405f205460805260405160805167fffffffffffffffe811161542a57600501541861542a575f680100000000000000046040516020525f5260405f20556060516080511015614a385760605167fffffffffffffffe811161542a576005015460a05260a05160805167fffffffffffffffe811161542a57600501556080516801000000000000000460a0516020525f5260405f20555b6060516801000000000000000555565b606051614aab57601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f825f031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60a0518060ff1c61542a5780670de0b6b3a764000003670de0b6b3a764000081135f83121861542a57905060c0526060518060ff1c61542a57602061572b5f395f516362ca4b1860e05260405161010052602060e0602460fc845afa614b13573d5f5f3e3d5ffd5b60203d1061542a5760e09050518060ff1c61542a5760c05180820281191515600160ff1b841415178215848484051417161561542a579050905005670de0b6b3a7640000810381811361542a57905060c05260805115614d4357602061572b5f395f5163b461100d61010052604051610120526040610100602461011c845afa614b9f573d5f5f3e3d5ffd5b60403d1061542a5761010090505160e052602061572b5f395f51638f8654c5610100526020610100600461011c845afa614bdb573d5f5f3e3d5ffd5b60203d1061542a5761010090505160e0511315614d4357602061572b5f395f516386fc88d3610160526020610160600461017c845afa614c1d573d5f5f3e3d5ffd5b60203d1061542a5761016090505161014052602061572b5f395f51632eb858e76101805260e0516101a0526020610180602461019c845afa614c61573d5f5f3e3d5ffd5b60203d1061542a576101809050516101605261016051610140511115614d435760c051606051602061570b5f395f5180820281158383830414171561542a5790509050610160516101405103602061572b5f395f5163544fb5c1610180526040516101a0526040610180602461019c845afa614cdf573d5f5f3e3d5ffd5b60403d1061542a5761018090506020810190505180820281158383830414171561542a579050905060206156cb5f395f5180820281158383830414171561542a5790509050048060ff1c61542a578082018281125f83121861542a579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff60405111614db057606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b60403661a4e037617c4051604052614dd161a520613355565b61a520805161a4e052602081015161a5005250617c805115614ea2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff617c405160405261a4e0516060526001608052617c805160a052614e3361a520614a48565b61a520511315614ea257600f61a540527f4e6f7420656e6f7567682072656b74000000000000000000000000000000000061a5605261a5405061a540518061a56001601f825f031636823750506308c379a061a50052602061a52052601f19601f61a54051011660440161a51cfd5b61a4e05161a52052670de0b6b3a764000061a4e051617ca05180820281158383830414171561542a57905090500461a4e05261a4e0511561542a5761a4e05161a520510361a52052602061572b5f395f5163f3fef3a361a5a052617c405161a5c052617ca051604052617c8051606052614f1d61a580614d4b565b61a5805161a5e052604061a5a0604461a5bc5f855af1614f3f573d5f5f3e3d5ffd5b60403d1061542a5761a5a09050604061a54060408360045afa5050617c605161a540511015614fcd57600861a580527f536c69707061676500000000000000000000000000000000000000000000000061a5a05261a5805061a580518061a5a001601f825f031636823750506308c379a061a54052602061a56052601f19601f61a58051011660440161a55cfd5b61a5405161a4e0518082811882841002189050905061a5805260206156eb604039602061572b6060393060805261a5805160a052615009613c4b565b61a5405161a4e051116150735760206156ab604039602061572b6060393360805261a5605160a052615039613c4b565b61a4e05161a5405111156152be5760206156eb604039602061572b6060393360805261a4e05161a540510360a0526152be613c4b566152be565b61a5405161a4e0510361a5a052617cc0516150ce5760206156ab604039602061572b6060393360805261a5605160a0526150ab613c4b565b60206156eb604039336060523060805261a5a05160a0526152be613c4b566152be565b60206156ab604039602061572b606039617cc05160805261a5605160a0526150f4613c4b565b7f750abc510000000000000000000000000000000000000000000000000000000061a5c0525f61a6005261a600805160208201209050617da051617dc0201861515d577f4ea696bb0000000000000000000000000000000000000000000000000000000061a5c0525b617cc05160405261a5c051606052617c405160805261a5405160a05261a5605160c05261a4e05160e052617ce05160208160051b018061010082617ce060045afa5050506020617da05101806101c082617da060045afa50506151c161a640613d3b565b61a640606061a5e060608360045afa505061a5a05161a60051101561524557601361a640527f6e6f7420656e6f7567682070726f63656564730000000000000000000000000061a6605261a6405061a640518061a66001601f825f031636823750506308c379a061a60052602061a62052601f19601f61a64051011660440161a61cfd5b61a5a05161a60051111561527a5760206156eb604039617cc0516060523360805261a5a05161a600510360a05261527a613c4b565b60206156eb604039617cc0516060523060805261a5a05160a05261529c613c4b565b60206156ab604039617cc0516060523360805261a6205160a0526152be613c4b565b680100000000000000075461a4e05180820182811061542a579050905068010000000000000007556001617c40516020525f5260405f2061a52051815561a50051600182015550617c40517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5605161a5a05261a4e05161a5c052604061a5a0a2617c4051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d061a5605161a5a05261a5405161a5c05261a4e05161a5e052606061a5a0a361a520516153cd57617c40517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a5a03760a061a5a0a2617c40516040526153cd614974565b60035461a5005180820281158383830414171561542a5790509050600454801561542a578082049050905061a5a05261a4e05161a5a05161a4e051808281188284110218905090500360035561a500516004556154286132c8565b565b5f80fd05dd545b0a02a9550b0800b3563b0703e655fb08005555d305000356730301c2554b090cf854ab0c00b1559308007c98ab23ad0005e4c4b3960b2c010536b7dbb70af600a524049e5708fa0045bcbaf487205700454f02c42000ef00057463129a019e0025e2d8ebee231d00254ba96d4606c701055457ff7b006800259a49719602f60045371fd8e60d5d00252a94394501da0005e1ec3c68008e00253d140d212eb50045e231bff001030005b4440df40dc50065627d2b83012b0005d14ff5b6097400254189617d2881002589fa4dd706ee0125720fb25406020065546e040d21fd0045a21adb9e024e002581d2f1b7298300250b8db6811a9f008590f8667d23ca00457128f3b800ad0025a757320604ac00451cf1f94703020065fadc9bfb065100859b6c56ec021f00256cce39be00db0005adddfa940a2100656f972f1208f00025c45a015501cc00052c5089c32657002522c714531aa900a520849f6a097e0045d0c581bf2ea1000531dc3ca8027f0005adfae4ce011700051b1800e32bf40005cc1891c72b2800255794e890145c00a55449b9cb013f00058908ea822327004524977ef30b0500e52621db2f01e80005e1270b6e015300457feb6395148300c52a0c35862a560045765337b601f60005acb708150d8c004586eba686060c0085d28952fc20df010551abf14a031300851b25cdaf21eb0025152f65cb144d006582bfb1de04b60065d9f11a64261f0005bc61ea2306b800c580e8f6ec23b900251e0cfcef2cac00051e4b77602f2a002569c6804e02040005fed9698c20d500c5dd171e7c0a170045ec74d0a82789002523cfed03064700658419568b8a182d185018601840184818401828184018381818190160a16576797065728300030a00290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000701eb05feba453b5fb58e87158f8fc74ed3fe9140000000000000000000000000000000000000000000000000226abadc42f800000000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000009090e71fc05dc66f67a086e0d69468f280be98a1
Deployed Bytecode
0x5f3560e01c60056005600983060261542e01601b395f5160088160ff16838360181c0260181c0660031b8260081c61ffff1601601839505f51818160201c14600336111661004c57612f71565b8061fffe1636103482600116021761542a578060101c61ffff16565b6004358060a01c61542a5760405260026040516020525f5260405f205460605260206060f35b60043567fffffffffffffffe811161542a576005015460405260206040f35b6004358060a01c61542a57604052680100000000000000046040516020525f5260405f205460605260206060f35b680100000000000000055460405260206040f35b680100000000000000065460405260206040f35b680100000000000000075460405260206040f35b680100000000000000085460405260206040f35b680100000000000000095460405260206040f35b6801000000000000000a5460405260206040f35b6004358060a01c61542a576040526024358060a01c61542a576060526801000000000000000b6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6004358060a01c61542a576040526801000000000000000c6040516020525f5260405f205460605260206060f35b602061568b60403960206040f35b602061572b60403960206040f35b60206156ab60403960206040f35b60206156eb60403960206040f35b5f5460021461542a5760025f556102196132c8565b60035f55005b6004358060a01c61542a5760e0525f5460021461542a57602060e051604052610249610100613355565b610100f35b6004358060a01c61542a576040525f5460021461542a5760016040516020525f5260405f2054151560605260206060f35b602061572b5f395f5163095a0fc6606052602060606004607c845afa6102a7573d5f5f3e3d5ffd5b60203d1061542a57606090505160405260035460605260045460805260605160405180820281158383830414171561542a5790509050608051801561542a578082049050905060a052602060a0f35b6040366102203761032a565b604435610220525f6102405261032a565b604435610220526064358060a01c61542a57610240525b5f5460021461542a5760046024351015610344575f61034c565b603260243511155b1561542a5760043560206156cb5f395f5180820281158383830414171561542a57905090506040526024356060526801000000000000000a546801000000000000000c610240516020525f5260405f205480820182811061542a57905090506080526103b961028061343f565b61028051610260526001670de0b6b3a7640000610260516103db6102a06139d2565b6102a05180820281158383830414171561542a579050905004600181811860018311021890500361028052602061570b5f395f51670de0b6b3a76400000261028051670de05bc096e9c000810281670de05bc096e9c00082041861542a57905004610280526102805160206156eb5f395f516370a082316102a052306102c05260206102a060246102bc845afa610474573d5f5f3e3d5ffd5b60203d1061542a576102a09050516102205180820182811061542a5790509050808281188284100218905090506102e05260206102e0f35b5f610220526104c6565b6044358060a01c61542a57610220525b5f5460021461542a5760326024351161542a57670de05bc096e9c00060206156cb5f395f51600435602061570b5f395f51670de0b6b3a76400000280820281158383830414171561542a57905090506105206102406139d2565b61024051801561542a5780820490509050670de0b6b3a7640000810281670de0b6b3a764000082041861542a579050670de0b6b3a76400006040526024356060526801000000000000000a546801000000000000000c610220516020525f5260405f205480820182811061542a57905090506080526105a061026061343f565b61026051801561542a5780820490509050600160206156cb5f395f51036107d060243501602435020180820182811061542a579050905004670de0b6b3a7640000810281670de0b6b3a764000082041861542a57905004610280526020610280f35b5f6103205261061c565b6064358060a01c61542a57610320525b5f5460021461542a5760206060600461016037610320516101c05261064261034061352c565b610340f35b3361050052610661565b6064358060a01c61542a57610500525b5f5460021461542a5760025f55326105005114610694576105005160405261068a6105206144e0565b610520511561542a575b6060600461032037600161038052610500516103a0526106b26140ad565b60035f55005b5f617d20523361a4605261071b565b60a43560040161271081351161542a5760208135018082617d203750503361a4605261071b565b60a43560040161271081351161542a5760208135018082617d2037505060c4358060a01c61542a5761a460525b6064358060a01c61542a57617c4052608435600401600581351161542a57803560208160051b018083617c60375050505f5460021461542a5760025f553261a460511461077e5761a4605160405261077461a4a06144e0565b61a4a0511561542a575b60206156eb604039617c405160605260243560805261079b613cc7565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610804577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa505061085e61a4e0613d3b565b61a4e06040810190505161a4c05260043561a4c05180820182811061542a57905090506103205260406024610340375f6103805261a460516103a0526108a26140ad565b60206156ab60403933606052602061572b60803960043560a0526108c4613c4b565b60206156ab604039617c4051606052602061572b60803961a4c05160a0526108ea613c4b565b60035f55005b336105805261090a565b6024358060a01c61542a57610580525b5f5460021461542a5760025f556004356109235761096e565b600435610320525f6103405261058051610360525f6103805261094461451a565b60206156ab60403933606052602061572b60803960043560a052610966613c4b565b61096e6132c8565b60035f55005b336105805261098e565b6024358060a01c61542a57610580525b5f5460021461542a5760025f556004356109a757610a11565b610580516040526109b96105a06144e0565b6105a0511561542a57600435610320525f6103405261058051610360526001610380526109e461451a565b60206156ab604039602061572b6060396105805160805260043560a052610a09613c4b565b610a116132c8565b60035f55005b3361058052610a31565b6044358060a01c61542a57610580525b5f5460021461542a5760025f55602435610a4a57610af0565b61058051604052610a5c6105a06144e0565b6105a0511561542a57604060046103203761058051610360525f61038052610a8261451a565b680100000000000000065460243580820182811061542a5790509050680100000000000000065560206156ab60403933606052602061572b60803960043560a052610acb613c4b565b60206156eb60403961058051606052602435608052610ae8613cc7565b610af06132c8565b60035f55005b5f617d20523361a46052610b59565b60843560040161271081351161542a5760208135018082617d203750503361a46052610b59565b60843560040161271081351161542a5760208135018082617d2037505060a4358060a01c61542a5761a460525b6044358060a01c61542a57617c4052606435600401600581351161542a57803560208160051b018083617c60375050505f5460021461542a5760025f55602435610ba257610d57565b61a46051604052610bb461a4a06144e0565b61a4a0511561542a5760206156eb604039617c4051606052602435608052610bda613cc7565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a4a0525f61a4e05261a4e0805160208201209050617d2051617d402018610c43577fe62214fe0000000000000000000000000000000000000000000000000000000061a4a0525b617c405160405261a4a05160605261a460516080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa5050610c9d61a4e0613d3b565b61a4e06040810190505161a4c05260043561a4c05180820182811061542a5790509050610320526024356103405261a46051610360525f61038052610ce061451a565b680100000000000000065460243580820182811061542a5790509050680100000000000000065560206156ab60403933606052602061572b60803960043560a052610d29613c4b565b60206156ab604039617c4051606052602061572b60803961a4c05160a052610d4f613c4b565b610d576132c8565b60035f55005b33610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610ddc565b6024358060a01c61542a57610320527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61034052610ddc565b6024358060a01c61542a5761032052604435610340525b5f5460021461542a5760025f55600435610df557611447565b6040366103603761032051604052610e0e6103a0613355565b6103a08051610360526020810151610380525061036051610e8e5760126103a0527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103c0526103a0506103a051806103c001601f825f031636823750506308c379a061036052602061038052601f19601f6103a051011660440161037cfd5b61036051600435808281188284100218905090506103a0526103a05161036051036103605261032051604052610ec56103e06144e0565b6103e0516103c0526103605161101857602061572b5f395f5163f3fef3a3610420526103205161044052670de0b6b3a7640000610460526040610420604461043c5f855af1610f16573d5f5f3e3d5ffd5b60403d1061542a57610420905060406103e060408360045afa50506103e05115610f69576103c0511561542a5760206156eb604039602061572b606039610320516080526103e05160a052610f69613c4b565b6104005115610f985760206156ab604039602061572b606039610320516080526104005160a052610f98613c4b565b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366104203760a0610420a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061040051610420526103a051610440526040610420a26103205160405261138661497456611386565b602061572b5f395f5163c16ef264610400526020610400600461041c845afa611043573d5f5f3e3d5ffd5b60203d1061542a576104009050516103e052610340516103e0511361542a57602061572b5f395f5163b461100d6104405261032051610460526040610440602461045c845afa611095573d5f5f3e3d5ffd5b60403d1061542a576104409050604061040060408360045afa5050610400516104205103610440526002610320516020525f5260405f2054610460526103e051610400511361118b57610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61048052610360516104a052610400516104c052610420516104e052610460516105005260a0610480a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610480526103a0516104a0526040610480a261134e565b602061572b5f395f5163f3fef3a36104c052610320516104e052670de0b6b3a76400006105005260406104c060446104dc5f855af16111cc573d5f5f3e3d5ffd5b60403d1061542a576104c09050604061048060408360045afa50506104a051610160526103605161018052600161044051015f811261542a576101a052610320516101c05261121c6104e061352c565b6104e0516104c0526104c051610440518082018281125f83121861542a57905090506104e052602061572b5f395f5163ab047e006105005261032051610520526104a051610540526104c051610560526104e05161058052803b1561542a575f610500608461051c5f855af1611294573d5f5f3e3d5ffd5b506103c051156112c257680100000000000000095461046052610460516002610320516020525f5260405f20555b610320517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26104a0516105005261036051610520526104c051610540526104e05161056052610460516105805260a0610500a2610320517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f610500526103a051610520526040610500a25b6103c05161138657600161032051604052610360516060525f6080526104605160a05261137c610480614a48565b610480511261542a575b60206156eb60403933606052306080526103a05160a0526113a5613c4b565b68010000000000000007546103a05180820182811061542a579050905068010000000000000007556001610320516020525f5260405f20610360518155610380516001820155506003546103805180820281158383830414171561542a5790509050600454801561542a57808204905090506103e0526103a0516103e0516103a0518082811882841102189050905003600355610380516004556114476132c8565b60035f55005b5f617d20523361a460526114b0565b60443560040161271081351161542a5760208135018082617d203750503361a460526114b0565b60443560040161271081351161542a5760208135018082617d203750506064358060a01c61542a5761a460525b6004358060a01c61542a57617c4052602435600401600581351161542a57803560208160051b018083617c60375050505f5460021461542a5760025f5561a460516040526114ff61a4a06144e0565b61a4a0511561542a57602061572b5f395f5163b461100d61a4e05261a4605161a50052604061a4e0602461a4fc845afa61153b573d5f5f3e3d5ffd5b60403d1061542a5761a4e09050604061a4a060408360045afa5050602061572b5f395f5163f3fef3a361a5205261a4605161a54052670de0b6b3a764000061a56052604061a520604461a53c5f855af1611597573d5f5f3e3d5ffd5b60403d1061542a5761a5209050604061a4e060408360045afa505060403661a5203761a460516040526115cb61a560613355565b61a560805161a52052602081015161a540525060206156ab604039602061572b606039617c405160805261a5005160a052611604613c4b565b7e8ae1880000000000000000000000000000000000000000000000000000000061a560525f61a5a05261a5a0805160208201209050617d2051617d40201861166c577fef67dc740000000000000000000000000000000000000000000000000000000061a560525b617c405160405261a5605160605261a4605160805261a4e05160a05261a5005160c05261a5205160e052617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506116d061a5e0613d3b565b61a5e0606061a58060608360045afa505061a5a05161a4e05180820182811061542a579050905061a5e05261a5e0511561542a575f61a6005261a5205161a5e05110156118d25761a4a05161a4c0510361a6205261a5805161a4a051131561542a5761a5a05161a6005261a5a05161a520510361a5205261a5c0516101605261a5205161018052600161a62051015f811261542a576101a05261a460516101c05261177c61a66061352c565b61a6605161a6405261a6405161a620518082018281125f83121861542a579050905061a66052602061572b5f395f5163ab047e0061a6805261a4605161a6a05261a5c05161a6c05261a6405161a6e05261a6605161a70052803b1561542a575f61a680608461a69c5f855af16117f4573d5f5f3e3d5ffd5b50680100000000000000095461a6805261a68051600261a460516020525f5260405f205560206156ab604039617c4051606052602061572b60803961a5c05160a05261183e613c4b565b60206156eb604039617c40516060523060805261a5a05160a052611860613c4b565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261a5c05161a6a05261a5205161a6c05261a6405161a6e05261a6605161a7005261a6805161a7205260a061a6a0a261a5005161a5c05180820382811161542a579050905061a500526119bc565b61a5205161a600525f61a5205261a460516040526118ee614974565b60206156eb604039617c40516060523060805261a5a05160a052611910613c4b565b60206156eb604039602061572b6060393060805261a4e05160a052611933613c4b565b61a6005161a5e05111156119645760206156eb60403961a4605160605261a6005161a5e05103608052611964613cc7565b60206156ab604039617c405160605261a4605160805261a5c05160a052611989613c4b565b61a460517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a6203760a061a620a25b61a460517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5005161a6205261a6005161a64052604061a620a2680100000000000000075461a6005180820182811061542a57905090506801000000000000000755600161a460516020525f5260405f2061a52051815561a5405160018201555060035461a5405180820281158383830414171561542a5790509050600454801561542a578082049050905061a6205261a6005161a6205161a60051808281188284110218905090500360035561a54051600455611a996132c8565b60035f55005b5f61036052611ab1565b608435610360525b6004358060a01c61542a57610320526064358060011c61542a57610340525f5460021461542a57602061572b5f395f5163b461100d6103c052610320516103e05260406103c060246103dc845afa611b0b573d5f5f3e3d5ffd5b60403d1061542a576103c09050604061038060408360045afa505061032051604052611b386103e0613355565b6103e0518060ff1c61542a576103c052610360516103e0525f610400526103c05115611b96576002610320516020525f5260405f20548060ff1c61542a57610400526001610380516103a05103015f811261542a576103e052611bd3565b68010000000000000009548060ff1c61542a57610400527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610380525b606036610420376103c0516044358082018281125f83121861542a57905090506103c05260016103c0511215611c68576011610480527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104a0526104805061048051806104a001601f825f031636823750506308c379a061044052602061046052601f19601f61048051011660440161045cfd5b602061572b5f395f5163c16ef2646104a05260206104a060046104bc845afa611c93573d5f5f3e3d5ffd5b60203d1061542a576104a090505161048052610480516103805113611d33576103805161042052602061572b5f395f516362ca4b186104a052610320516104c05260206104a060246104bc845afa611ced573d5f5f3e3d5ffd5b60203d1061542a576104a0905051602061570b5f395f51670de0b6b3a76400000280820281158383830414171561542a57905090508060ff1c61542a5761046052611e18565b602061572b5f395f5163544fb5c16104a052610320516104c05260406104a060246104bc845afa611d66573d5f5f3e3d5ffd5b60403d1061542a576104a09050602081019050518060ff1c61542a576024358082018281125f83121861542a579050905061044052610440515f811261542a57610160526103c0515f811261542a57610180526103e0516101a052610320516101c052611dd46104a061352c565b6104a051610420526104405160206156cb5f395f518060ff1c61542a5780820281191515600160ff1b841415178215848484051417161561542a5790509050610440525b6103c051602061570b5f395f518060ff1c61542a5780820281191515600160ff1b841415178215848484051417161561542a57905090506103c052602061572b5f395f51632eb858e76104c052610420516104e05260206104c060246104dc845afa611e86573d5f5f3e3d5ffd5b60203d1061542a576104c09050518060ff1c61542a576104a05261048051610380511315611f0957610440515f811261542a576040526103e0516060525f608052611ed26104c061343f565b6104c0518060ff1c61542a576104a05180820281191515600160ff1b841415178215848484051417161561542a5790509050610460525b6103c05161046051056104c0526104c051670de0b6b3a76400006104c0516104005180820281191515600160ff1b841415178215848484051417161561542a5790509050058082038281135f83121861542a5790509050670de0b6b3a7640000810381811361542a5790506104c05261034051156120505761048051610420511315612050576104a051602061572b5f395f516386fc88d3610500526020610500600461051c845afa611fbe573d5f5f3e3d5ffd5b60203d1061542a576105009050518060ff1c61542a57808281188284130218905090506104a0518082038281135f83121861542a57905090506104e05260016104e05112612050576104c0516103c0516104e0516104405180820281191515600160ff1b841415178215848484051417161561542a5790509050058082018281125f83121861542a57905090506104c0525b60206104c0f35b6004358060a01c61542a5761a6a0525f5460021461542a5760025f555f61a6c0523361a6a0511461209757600261a6a0516020525f5260405f205461a6c0525b61a6a051617c4052602435617c605261a6c051617c8052670de0b6b3a7640000617ca052604036617cc0375f617da0526120cf614db8565b60035f55005b5f61a7a0526120fd565b60a43560040161271081351161542a576020813501808261a7a03750505b6004358060a01c61542a5761a6a0526064358060a01c61542a5761a6c052608435600401600581351161542a57803560208160051b01808361a6e0375050505f5460021461542a5760025f555f61cf00523361a6a0511461216d57600261a6a0516020525f5260405f205461cf00525b61a6a051617c4052602435617c605261cf0051617c8052604435670de0b6b3a7640000818118670de0b6b3a7640000831002189050617ca05261a6c051617cc05261a6e05160208160051b0180617ce08261a6e060045afa505050602061a7a0510180617da08261a7a060045afa50506121e5614db8565b60035f55005b670de0b6b3a764000061010052612205565b602435610100525b6004358060a01c61542a5760e0525f5460021461542a575f610120523360e0511461223e57600260e0516020525f5260405f2054610120525b670de0b6b3a7640000602061572b5f395f5163544fb5c16101605260e051610180526040610160602461017c845afa612279573d5f5f3e3d5ffd5b60403d1061542a5761016090505161010051604052610120516060526122a06101c0614d4b565b6101c05180820281158383830414171561542a57905090500461014052670de0b6b3a764000060e0516040526122d7610180613355565b610180516101005180820281158383830414171561542a579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f35b5f61020052612337565b6024358060011c61542a57610200525b6004358060a01c61542a576101e0525f5460021461542a5760206101e051610280526101e05160405261236b610220613355565b610220516102a052610200516102c05260026101e0516020525f5260405f20546102e05260806040608061028060045afa506123a8610260614a48565b610260f35b6040366101e0376123d3565b6004356101e0525f610200526123d3565b604060046101e0375b5f5460021461542a576801000000000000000554610220526102005161024052610200516124045761022051610240525b6101e051610260525f610280525f620f4240905b80620273a0526102205161026051101561243c5761024051620273a051181561243f565b60015b15612449576125b1565b6102605167fffffffffffffffe811161542a5760050154620273c052620273c05160405261247962027400613355565b6202740051620273e052620273c051604052620273e05160605260016080526002620273c0516020525f5260405f205460a0526124b862027420614a48565b620274205162027400527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff62027400511361259157602061572b5f395f5163544fb5c16202746052620273c051620274805260406202746060246202747c845afa612525573d5f5f3e3d5ffd5b60403d1061542a5762027460905060406202742060408360045afa5050610280516103e7811161542a5760a081026102a001620273c05181526202742051602082015262027440516040820152620273e051606082015262027400516080820152506001810161028052505b610260516001810181811061542a57905061026052600101818118612418575b5050602080620273a05280620273a0015f6102805180835260a081025f826103e8811161542a57801561260957905b60a081026102a00160a08202602088010160a08160a08460045afa5050506001018181186125e0575b50508201602001915050905081019050620273a0f35b6020602061572b5f395f5163f2388acb604052602060406004605c845afa612649573d5f5f3e3d5ffd5b60203d1061542a5760409050f35b6004358060a01c61542a576040525f5460021461542a57602061572b5f395f5163e8dd1ef1606052604051608052602060606024607c845afa61269c573d5f5f3e3d5ffd5b60203d1061542a576060518060011c61542a5760a05260a09050511561542a57602061572b5f395f5163b461100d60a05260405160c052604060a0602460bc845afa6126ea573d5f5f3e3d5ffd5b60403d1061542a5760a090506040606060408360045afa5050602061572b5f395f51632eb858e760a05260605160c052602060a0602460bc845afa612731573d5f5f3e3d5ffd5b60203d1061542a5760a090505161012052602061572b5f395f516324299b7a60e05260805161010052602060e0602460fc845afa612771573d5f5f3e3d5ffd5b60203d1061542a5760e0905051610140526040610120f35b6004358060a01c61542a5760e0525f5460021461542a57602061572b5f395f5163544fb5c16101405260e051610160526040610140602461015c845afa6127d2573d5f5f3e3d5ffd5b60403d1061542a576101409050604061010060408360045afa5050602061572b5f395f5163b461100d6101805260e0516101a0526040610180602461019c845afa61281f573d5f5f3e3d5ffd5b60403d1061542a576101809050604061014060408360045afa5050610120516101c052610100516101e05260e05160405261285b610180613355565b61018051610200526001610140516101605103015f811261542a576102205260806101c0f35b602061568b5f395f5163f851a440604052602060406004605c845afa6128a9573d5f5f3e3d5ffd5b60203d1061542a576040518060a01c61542a576080526080905051331861542a5760206157cb5f395f5160043511156128e2575f6128ec565b620f424060043510155b61294b5760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b602061572b5f395f51631aa02d59604052600435606052803b1561542a575f60406024605c5f855af1612980573d5f5f3e3d5ffd5b50005b6004358060a01c61542a576040525f5460021461542a5760025f55602061568b5f395f5163f851a440606052602060606004607c845afa6129c6573d5f5f3e3d5ffd5b60203d1061542a576060518060a01c61542a5760a05260a0905051331861542a57604051680100000000000000085560405163e91f2f4c606052602060606004607c5f855af1612a18573d5f5f3e3d5ffd5b60203d1061542a57606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a160035f55005b5f5460021461542a5760025f55602061568b5f395f5163f851a440604052602060406004605c845afa612a8b573d5f5f3e3d5ffd5b60203d1061542a576040518060a01c61542a576080526080905051331861542a57602435600435111561542a57662386f26fc100006024351061542a576706f05b59d3b200006004351161542a5760243568010000000000000009556004356801000000000000000a557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a160035f55005b6004358060a01c61542a576040525f5460021461542a5760025f55602061568b5f395f5163f851a440606052602060606004607c845afa612b6b573d5f5f3e3d5ffd5b60203d1061542a576060518060a01c61542a5760a05260a0905051331861542a57602061572b5f395f5163cc1891c7606052604051608052803b1561542a575f60606024607c5f855af1612bc1573d5f5f3e3d5ffd5b507fc770618faa286dffef5f2a2fea0c0685d5e0d18110242589ab70e5ae36f94ab560405160605260206060a160035f55005b602061572b5f395f5163095a0fc6606052602060606004607c845afa612c1c573d5f5f3e3d5ffd5b60203d1061542a57606090505160405260035460605260045460805260605160405180820281158383830414171561542a5790509050608051801561542a5780820490509050680100000000000000075480820182811061542a5790509050606052680100000000000000065460a05260a05160605160a051808281188284110218905090500360c052602060c0f35b5f5460021461542a5760025f55602061568b5f395f5163cab4d3db610140526020610140600461015c845afa612ce4573d5f5f3e3d5ffd5b60203d1061542a57610140518060a01c61542a576101805261018090505161012052602061572b5f395f5163095a0fc6610160526020610160600461017c845afa612d31573d5f5f3e3d5ffd5b60203d1061542a57610160905051610140526003546101605260045461018052610160516101405180820281158383830414171561542a579050905061018051801561542a57808204905090506101605261014051610180526101605160035561018051600455612da06132c8565b61016051680100000000000000075480820182811061542a57905090506101a05268010000000000000006546101c0526101c0516101a05111612e24577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f5f6101e052610160516102005260406101e0a15f6101e05260206101e0612e9b56612e9b565b6101a05168010000000000000006556101c0516101a051036101a05260206156eb604039610120516060526101a051608052612e5e613cc7565b7f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f6101a0516101e052610160516102005260406101e0a160206101a05b60035f55f35b5f5460021461542a57600160405260206040f35b6004358060a01c61542a576040526024358060011c61542a576060526060516801000000000000000b336020525f5260405f20806040516020525f5260405f20905055604051337f1d3e246ebbc933bf65d3290db9f93d67ab91a12d2b19308a35806e04d1c174c560605160805260206080a3005b6004356801000000000000000c336020525f5260405f2055337f06159bbd6e0c9c85edd120aebed689bc1c6bd79decfedbdf2f8c17abf681ca3c60043560405260206040a2005b5f5ffd5b6040516060525f60805260405160801c15612f995760405160801c60605260806080525b60605160401c15612fb75760605160401c6060526040608051016080525b60605160201c15612fd55760605160201c6060526020608051016080525b60605160101c15612ff35760605160101c6060526010608051016080525b60605160081c156130115760605160081c6060526008608051016080525b60605160041c1561302f5760605160041c6060526004608051016080525b60605160021c1561304d5760605160021c6060526002608051016080525b60605160011c15613062576001608051016080525b608051815250565b60a0518060ff1c61542a5760c05260a0511561542a57606060a051604052613093610100612f75565b610100518060ff1c61542a570360e05260c05160e051609f035f811261542a571b609f1c8060ff1c61542a5760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b680100000000000000085463e91f2f4c606052602060606004607c5f855af16132f3573d5f5f3e3d5ffd5b60203d1061542a576060905051640a3c2abcef818118640a3c2abcef831002189050604052602061572b5f395f5163d4387a99606052604051608052602060606024607c5f855af1613347573d5f5f3e3d5ffd5b60203d1061542a5760605050565b602061572b5f395f5163095a0fc6608052602060806004609c845afa61337d573d5f5f3e3d5ffd5b60203d1061542a57608090505160605260016040516020525f5260405f208054608052600181015460a052506080516133c5575f815260605160208201525061343d5661343d565b60805160605180820281158383830414171561542a579050905060c05260c05160a051801561542a5780820690509050156134245760026801000000000000000554106134245760c05160a05180820182811061542a579050905060c0525b60a05160c0510460c05260c05181526060516020820152505b565b60605160206157ab5f395f5102604051608051606051604051046103e88181186103e8831102189050683635c9adc5dea000000480820182811061542a5790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a76400000380820281158383830414171561542a57905090500460a05260a05160c052600160318101905b8060e05260605160e051186134e057613522565b602061574b5f395f5160a051602061576b5f395f5180820281158383830414171561542a57905090500460a05260a05160c0510160c0526001018181186134cc575b505060c051815250565b610180516135995760076101e0527f4e6f206c6f616e00000000000000000000000000000000000000000000000000610200526101e0506101e0518061020001601f825f031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b602061572b5f395f51638f8654c5610200526020610200600461021c845afa6135c4573d5f5f3e3d5ffd5b60203d1061542a576102009050516101e052602061572b5f395f51632eb858e7610220526101e051610240526020610220602461023c845afa613609573d5f5f3e3d5ffd5b60203d1061542a57610220905051610200526101605160206156cb5f395f5180820281158383830414171561542a57905090506040526101a0516060526801000000000000000a546801000000000000000c6101c0516020525f5260405f205480820182811061542a579050905060805261368561024061343f565b610240516102205261018051602061570b5f395f5180820281158383830414171561542a57905090506001810181811061542a579050610220516102005180820281158383830414171561542a579050905004610220526102205161374957600e610240527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006102605261024050610240518061026001601f825f031636823750506308c379a061020052602061022052601f19601f61024051011660440161021cfd5b6102205160a05261375b61026061306a565b61026051610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61024051136137b457610240516001602061578b5f395f51038082038281135f83121861542a5790509050610240525b602061578b5f395f51610240510561024052610240516101a0518060ff1c61542a57806104000361040081135f83121861542a579050808281188284120218905090506101e0518082018281125f83121861542a5790509050610240526101e05161024051136138e557602061572b5f395f5163ec65470661026052610240516001810381811361542a579050610280526020610260602461027c845afa61385e573d5f5f3e3d5ffd5b60203d1061542a57610260518060011c61542a576102a0526102a09050516138e557600d6102c0527f4465627420746f6f2068696768000000000000000000000000000000000000006102e0526102c0506102c051806102e001601f825f031636823750506308c379a06102805260206102a052601f19601f6102c051011660440161029cfd5b602061572b5f395f516386fc88d36102a05260206102a060046102bc845afa613910573d5f5f3e3d5ffd5b60203d1061542a576102a0905051602061572b5f395f51632eb858e76102605261024051610280526020610260602461027c845afa613951573d5f5f3e3d5ffd5b60203d1061542a57610260905051106139c957600d6102e0527f4465627420746f6f206869676800000000000000000000000000000000000000610300526102e0506102e0518061030001601f825f031636823750506308c379a06102a05260206102c052601f19601f6102e05101166044016102bcfd5b61024051815250565b602061572b5f395f516386fc88d3610180526020610180600461019c845afa6139fd573d5f5f3e3d5ffd5b60203d1061542a5761018090505161016052602061572b5f395f5163a7db79a56101a05260206101a060046101bc845afa613a3a573d5f5f3e3d5ffd5b60203d1061542a576101a0905051670de0b6b3a7640000810281670de0b6b3a764000082041861542a57905061016051801561542a578082049050905060a052613a856101e061306a565b6101e051610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113613ae85761018051602061578b5f395f516001810381811361542a5790508082038281135f83121861542a5790509050610180525b602061578b5f395f5161018051056005810181811261542a57905061018052602061572b5f395f5163c16ef2646101c05260206101c060046101dc845afa613b32573d5f5f3e3d5ffd5b60203d1061542a576101c09050516101a052610180516101a0516001810181811261542a5790508082811882841302189050905061018052602061572b5f395f51632eb858e76101e052610180516102005260206101e060246101fc845afa613b9d573d5f5f3e3d5ffd5b60203d1061542a576101e09050516101c0525f610401905b806101e052610180516001810381811361542a579050610180526101a0516101805113613be157613c3f565b6101c05161020052602061576b5f395f516101c051602061574b5f395f5180820281158383830414171561542a5790509050046101c052610160516101c0511115613c3457610200518352505050613c49565b600101818118613bb5575b50506101c0518152505b565b60a05115613cc5576040516323b872dd60c05260605160e0526080516101005260a05161012052602060c0606460dc5f855af1613c8a573d5f5f3e3d5ffd5b3d613ca157803b1561542a57600161014052613cb9565b60203d1061542a5760c0518060011c61542a57610140525b6101409050511561542a575b565b60805115613d395760405163a9059cbb60a05260605160c05260805160e052602060a0604460bc5f855af1613cfe573d5f5f3e3d5ffd5b3d613d1557803b1561542a57600161010052613d2d565b60203d1061542a5760a0518060011c61542a57610100525b6101009050511561542a575b565b60206156ab5f395f516040511461542a5760206156eb5f395f516040511461542a5760603661290037602061572b5f395f51638f8654c5612960526020612960600461297c845afa613d8f573d5f5f3e3d5ffd5b60203d1061542a5761296090505161290052602061572b5f395f5163ebcb006761298052612900516129a0526020612980602461299c845afa613dd4573d5f5f3e3d5ffd5b60203d1061542a5761298090505161296052602061572b5f395f516331f7e3066129a052612900516129c05260206129a060246129bc845afa613e19573d5f5f3e3d5ffd5b60203d1061542a576129a0905051612980526040515a5f60605181615300015260048101905060c0608051612a205260a051612a405260c051612a605260e051612a805280612aa05280612a20015f610100518083528060051b5f826005811161542a578015613ea357905b8060051b61012001518160051b602088010152600101818118613e85575b5050820160200191505090508101905080612ac05280612a200160206101c051018082826101c060045afa50508051806020830101601f825f03163682375050601f19601f82516020010116905081019050612a0052612a0080516020820183615300018281848460045afa50505080830192505050806152e0526152e050506040617c006152e0516153005f8686f190509050613f43573d5f5f3e3d5ffd5b3d604081183d6040100218617be052617be06020815101806129a0828460045afa5050506129a05160201161542a576129c051612a20526020612a0052612a006020810151815160200360031b1c9050612920526129a05160401161542a576129e051612a20526020612a0052612a006020810151815160200360031b1c905061294052602061572b5f395f51638f8654c5612a00526020612a006004612a1c845afa613ff2573d5f5f3e3d5ffd5b60203d1061542a57612a00905051612900511861542a57602061572b5f395f5163ebcb0067612a005261290051612a20526020612a006024612a1c845afa61403c573d5f5f3e3d5ffd5b60203d1061542a57612a00905051612960511861542a57602061572b5f395f516331f7e306612a005261290051612a20526020612a006024612a1c845afa614086573d5f5f3e3d5ffd5b60203d1061542a57612a00905051612980511861542a57606081606061290060045afa5050565b60016103a0516020525f5260405f2054156141275760146103c0527f4c6f616e20616c726561647920637265617465640000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b600461036051101561419857600f6103c0527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b603261036051111561420957600f6103c0527f4e656564206c657373207469636b7300000000000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b61032051610160526103405161018052610360516101a0526103a0516101c0526142346103e061352c565b6103e0516103c0526103c051600161036051038060ff1c61542a578082018281125f83121861542a57905090506103e052602061572b5f395f5163095a0fc6610420526020610420600461043c845afa614290573d5f5f3e3d5ffd5b60203d1061542a576104209050516104005260016103a0516020525f5260405f20610340518155610400516001820155506801000000000000000954610420526104205160026103a0516020525f5260405f20556801000000000000000554610440526103a0516104405167fffffffffffffffe811161542a576005015561044051680100000000000000046103a0516020525f5260405f20556001610440510168010000000000000005556003546104005180820281158383830414171561542a5790509050600454801561542a57808204905090506103405180820182811061542a579050905060035561040051600455602061572b5f395f5163ab047e00610460526103a05161048052610320516104a0526103c0516104c0526103e0516104e052803b1561542a575f610460608461047c5f855af16143d5573d5f5f3e3d5ffd5b5068010000000000000006546103405180820182811061542a5790509050680100000000000000065561038051156144485760206156ab60403933606052602061572b6080396103205160a05261442a613c4b565b60206156eb6040396103a05160605261034051608052614448613cc7565b6144506132c8565b6103a0517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610320516104605261034051610480526103c0516104a0526103e0516104c052610420516104e05260a0610460a26103a0517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516104605261034051610480526040610460a2565b60405133186144f0576001614515565b6801000000000000000b6040516020525f5260405f2080336020525f5260405f209050545b815250565b6040366103a037610360516040526145336103e0613355565b6103e080516103a05260208101516103c052506103a0516145b35760126103e0527f4c6f616e20646f65736e27742065786973740000000000000000000000000000610400526103e0506103e0518061040001601f825f031636823750506308c379a06103a05260206103c052601f19601f6103e05101166044016103bcfd5b6103a0516103405180820182811061542a57905090506103a052602061572b5f395f5163b461100d6104205261036051610440526040610420602461043c845afa614600573d5f5f3e3d5ffd5b60403d1061542a57610420905060406103e060408360045afa505060016103e0516104005103015f811261542a5761042052602061572b5f395f5163f3fef3a361048052610360516104a052670de0b6b3a76400006104c0526040610480604461049c5f855af1614673573d5f5f3e3d5ffd5b60403d1061542a576104809050604061044060408360045afa505061044051156146fc57601a610480527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104a0526104805061048051806104a001601f825f031636823750506308c379a061044052602061046052601f19601f61048051011660440161045cfd5b6103805161472357610460516103205180820182811061542a57905090506104605261473e565b610460516103205180820382811161542a5790509050610460525b61046051610160526103a05161018052610420516101a052610360516101c0526147696104a061352c565b6104a05161048052610480516103e05161040051038082018281125f83121861542a57905090506104a052602061572b5f395f5163ab047e006104c052610360516104e052610460516105005261048051610520526104a05161054052803b1561542a575f6104c060846104dc5f855af16147e6573d5f5f3e3d5ffd5b506001610360516020525f5260405f206103a05181556103c0516001820155505f6104c05233610360511861483d5768010000000000000009546104c0526104c0516002610360516020525f5260405f2055614852565b6002610360516020525f5260405f20546104c0525b61034051156148a3576003546103c05180820281158383830414171561542a5790509050600454801561542a57808204905090506103405180820182811061542a57905090506003556103c0516004555b610380516148eb57610360517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610320516104e052610340516105005260406104e0a261491f565b610360517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb54610320516104e05260206104e0a25b610360517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610460516104e0526103a0516105005261048051610520526104a051610540526104c0516105605260a06104e0a2565b68010000000000000005546001810381811161542a579050606052680100000000000000046040516020525f5260405f205460805260405160805167fffffffffffffffe811161542a57600501541861542a575f680100000000000000046040516020525f5260405f20556060516080511015614a385760605167fffffffffffffffe811161542a576005015460a05260a05160805167fffffffffffffffe811161542a57600501556080516801000000000000000460a0516020525f5260405f20555b6060516801000000000000000555565b606051614aab57601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f825f031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60a0518060ff1c61542a5780670de0b6b3a764000003670de0b6b3a764000081135f83121861542a57905060c0526060518060ff1c61542a57602061572b5f395f516362ca4b1860e05260405161010052602060e0602460fc845afa614b13573d5f5f3e3d5ffd5b60203d1061542a5760e09050518060ff1c61542a5760c05180820281191515600160ff1b841415178215848484051417161561542a579050905005670de0b6b3a7640000810381811361542a57905060c05260805115614d4357602061572b5f395f5163b461100d61010052604051610120526040610100602461011c845afa614b9f573d5f5f3e3d5ffd5b60403d1061542a5761010090505160e052602061572b5f395f51638f8654c5610100526020610100600461011c845afa614bdb573d5f5f3e3d5ffd5b60203d1061542a5761010090505160e0511315614d4357602061572b5f395f516386fc88d3610160526020610160600461017c845afa614c1d573d5f5f3e3d5ffd5b60203d1061542a5761016090505161014052602061572b5f395f51632eb858e76101805260e0516101a0526020610180602461019c845afa614c61573d5f5f3e3d5ffd5b60203d1061542a576101809050516101605261016051610140511115614d435760c051606051602061570b5f395f5180820281158383830414171561542a5790509050610160516101405103602061572b5f395f5163544fb5c1610180526040516101a0526040610180602461019c845afa614cdf573d5f5f3e3d5ffd5b60403d1061542a5761018090506020810190505180820281158383830414171561542a579050905060206156cb5f395f5180820281158383830414171561542a5790509050048060ff1c61542a578082018281125f83121861542a579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff60405111614db057606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b60403661a4e037617c4051604052614dd161a520613355565b61a520805161a4e052602081015161a5005250617c805115614ea2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff617c405160405261a4e0516060526001608052617c805160a052614e3361a520614a48565b61a520511315614ea257600f61a540527f4e6f7420656e6f7567682072656b74000000000000000000000000000000000061a5605261a5405061a540518061a56001601f825f031636823750506308c379a061a50052602061a52052601f19601f61a54051011660440161a51cfd5b61a4e05161a52052670de0b6b3a764000061a4e051617ca05180820281158383830414171561542a57905090500461a4e05261a4e0511561542a5761a4e05161a520510361a52052602061572b5f395f5163f3fef3a361a5a052617c405161a5c052617ca051604052617c8051606052614f1d61a580614d4b565b61a5805161a5e052604061a5a0604461a5bc5f855af1614f3f573d5f5f3e3d5ffd5b60403d1061542a5761a5a09050604061a54060408360045afa5050617c605161a540511015614fcd57600861a580527f536c69707061676500000000000000000000000000000000000000000000000061a5a05261a5805061a580518061a5a001601f825f031636823750506308c379a061a54052602061a56052601f19601f61a58051011660440161a55cfd5b61a5405161a4e0518082811882841002189050905061a5805260206156eb604039602061572b6060393060805261a5805160a052615009613c4b565b61a5405161a4e051116150735760206156ab604039602061572b6060393360805261a5605160a052615039613c4b565b61a4e05161a5405111156152be5760206156eb604039602061572b6060393360805261a4e05161a540510360a0526152be613c4b566152be565b61a5405161a4e0510361a5a052617cc0516150ce5760206156ab604039602061572b6060393360805261a5605160a0526150ab613c4b565b60206156eb604039336060523060805261a5a05160a0526152be613c4b566152be565b60206156ab604039602061572b606039617cc05160805261a5605160a0526150f4613c4b565b7f750abc510000000000000000000000000000000000000000000000000000000061a5c0525f61a6005261a600805160208201209050617da051617dc0201861515d577f4ea696bb0000000000000000000000000000000000000000000000000000000061a5c0525b617cc05160405261a5c051606052617c405160805261a5405160a05261a5605160c05261a4e05160e052617ce05160208160051b018061010082617ce060045afa5050506020617da05101806101c082617da060045afa50506151c161a640613d3b565b61a640606061a5e060608360045afa505061a5a05161a60051101561524557601361a640527f6e6f7420656e6f7567682070726f63656564730000000000000000000000000061a6605261a6405061a640518061a66001601f825f031636823750506308c379a061a60052602061a62052601f19601f61a64051011660440161a61cfd5b61a5a05161a60051111561527a5760206156eb604039617cc0516060523360805261a5a05161a600510360a05261527a613c4b565b60206156eb604039617cc0516060523060805261a5a05160a05261529c613c4b565b60206156ab604039617cc0516060523360805261a6205160a0526152be613c4b565b680100000000000000075461a4e05180820182811061542a579050905068010000000000000007556001617c40516020525f5260405f2061a52051815561a50051600182015550617c40517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5605161a5a05261a4e05161a5c052604061a5a0a2617c4051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d061a5605161a5a05261a5405161a5c05261a4e05161a5e052606061a5a0a361a520516153cd57617c40517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a5a03760a061a5a0a2617c40516040526153cd614974565b60035461a5005180820281158383830414171561542a5790509050600454801561542a578082049050905061a5a05261a4e05161a5a05161a4e051808281188284110218905090500360035561a500516004556154286132c8565b565b5f80fd05dd545b0a02a9550b0800b3563b0703e655fb08005555d305000356730301c2554b090cf854ab0c00b1559308007c98ab23ad0005e4c4b3960b2c010536b7dbb70af600a524049e5708fa0045bcbaf487205700454f02c42000ef00057463129a019e0025e2d8ebee231d00254ba96d4606c701055457ff7b006800259a49719602f60045371fd8e60d5d00252a94394501da0005e1ec3c68008e00253d140d212eb50045e231bff001030005b4440df40dc50065627d2b83012b0005d14ff5b6097400254189617d2881002589fa4dd706ee0125720fb25406020065546e040d21fd0045a21adb9e024e002581d2f1b7298300250b8db6811a9f008590f8667d23ca00457128f3b800ad0025a757320604ac00451cf1f94703020065fadc9bfb065100859b6c56ec021f00256cce39be00db0005adddfa940a2100656f972f1208f00025c45a015501cc00052c5089c32657002522c714531aa900a520849f6a097e0045d0c581bf2ea1000531dc3ca8027f0005adfae4ce011700051b1800e32bf40005cc1891c72b2800255794e890145c00a55449b9cb013f00058908ea822327004524977ef30b0500e52621db2f01e80005e1270b6e015300457feb6395148300c52a0c35862a560045765337b601f60005acb708150d8c004586eba686060c0085d28952fc20df010551abf14a031300851b25cdaf21eb0025152f65cb144d006582bfb1de04b60065d9f11a64261f0005bc61ea2306b800c580e8f6ec23b900251e0cfcef2cac00051e4b77602f2a002569c6804e02040005fed9698c20d500c5dd171e7c0a170045ec74d0a82789002523cfed0306470065000000000000000000000000040efc9a141d7fa47745751c253e02d065c90bdb000000000000000000000000331b9182088e2a7d6d3fe4742aba1fb231aecc560000000000000000000000000000000000000000000000000000000000000001000000000000000000000000b102f7efa0d5de071a8d37b3548e1c7cb148caf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000009090e71fc05dc66f67a086e0d69468f280be98a10000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000a545b52edd08380000000000000000000000000000000000000000000000000e34518160e6d982000000000000000000000000000000000000000000000000016345785d8a0000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$1,120.39
Net Worth in FRAX
1,303.939043
Token Allocations
CRVUSD
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| FRAXTAL | 100.00% | $0.998444 | 1,122.1342 | $1,120.39 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.