More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 796 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Borrow_more | 21726055 | 4 days ago | IN | 0 ETH | 0.0038523 | ||||
Repay_extended | 21713127 | 6 days ago | IN | 0 ETH | 0.00487551 | ||||
Repay_extended | 21646956 | 15 days ago | IN | 0 ETH | 0.01016638 | ||||
Repay | 21641005 | 16 days ago | IN | 0 ETH | 0.0014966 | ||||
Remove_collatera... | 21640992 | 16 days ago | IN | 0 ETH | 0.00267819 | ||||
Repay | 21640987 | 16 days ago | IN | 0 ETH | 0.00227049 | ||||
Remove_collatera... | 21640968 | 16 days ago | IN | 0 ETH | 0.00174217 | ||||
Repay | 21640959 | 16 days ago | IN | 0 ETH | 0.00162311 | ||||
Remove_collatera... | 21640934 | 16 days ago | IN | 0 ETH | 0.00184228 | ||||
Repay | 21640925 | 16 days ago | IN | 0 ETH | 0.00182426 | ||||
Remove_collatera... | 21640914 | 16 days ago | IN | 0 ETH | 0.00188681 | ||||
Repay | 21640911 | 16 days ago | IN | 0 ETH | 0.00159835 | ||||
Remove_collatera... | 21640895 | 16 days ago | IN | 0 ETH | 0.00182467 | ||||
Create_loan | 21636863 | 17 days ago | IN | 0 ETH | 0.00479708 | ||||
Remove_collatera... | 21634750 | 17 days ago | IN | 0 ETH | 0.00187435 | ||||
Repay | 21634743 | 17 days ago | IN | 0 ETH | 0.00220699 | ||||
Borrow_more | 21632649 | 17 days ago | IN | 0 ETH | 0.00360569 | ||||
Repay | 21630072 | 18 days ago | IN | 0 ETH | 0.00111066 | ||||
Create_loan_exte... | 21616170 | 20 days ago | IN | 0 ETH | 0.00967086 | ||||
Remove_collatera... | 21600122 | 22 days ago | IN | 0 ETH | 0.00129354 | ||||
Remove_collatera... | 21600122 | 22 days ago | IN | 0 ETH | 0.00209819 | ||||
Remove_collatera... | 21600122 | 22 days ago | IN | 0 ETH | 0.00150006 | ||||
Remove_collatera... | 21600122 | 22 days ago | IN | 0 ETH | 0.00127062 | ||||
Remove_collatera... | 21600122 | 22 days ago | IN | 0 ETH | 0.00127062 | ||||
Remove_collatera... | 21600122 | 22 days ago | IN | 0 ETH | 0.00150002 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19801071 | 273 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
crvUSD Controller
Compiler Version
vyper:0.3.10
Contract Source Code (Vyper language format)
# @version 0.3.10 """ @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 interface PriceOracle: def price() -> uint256: view def price_w() -> uint256: nonpayable event UserState: user: indexed(address) collateral: uint256 debt: uint256 n1: int256 n2: int256 liquidation_discount: uint256 event Borrow: user: indexed(address) collateral_increase: uint256 loan_increase: uint256 event Repay: user: indexed(address) collateral_decrease: uint256 loan_decrease: uint256 event RemoveCollateral: user: indexed(address) collateral_decrease: uint256 event Liquidate: liquidator: indexed(address) user: indexed(address) collateral_received: uint256 stablecoin_received: uint256 debt: uint256 event SetMonetaryPolicy: monetary_policy: address event SetBorrowingDiscounts: loan_discount: uint256 liquidation_discount: uint256 event CollectFees: amount: uint256 new_supply: uint256 struct Loan: initial_debt: uint256 rate_mul: uint256 struct Position: user: address x: uint256 y: uint256 debt: uint256 health: int256 struct CallbackData: active_band: int256 stablecoins: uint256 collateral: uint256 FACTORY: immutable(Factory) MAX_LOAN_DISCOUNT: constant(uint256) = 5 * 10**17 MIN_LIQUIDATION_DISCOUNT: constant(uint256) = 10**16 # Start liquidating when threshold reached MAX_TICKS: constant(int256) = 50 MAX_TICKS_UINT: constant(uint256) = 50 MIN_TICKS: constant(int256) = 4 MAX_SKIP_TICKS: constant(uint256) = 1024 MAX_P_BASE_BANDS: constant(int256) = 5 MAX_RATE: constant(uint256) = 43959106799 # 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 MAX_ETH_GAS: constant(uint256) = 10000 # Forward this much gas to ETH transfers (2300 is what send() does) @external def __init__( collateral_token: address, monetary_policy: address, loan_discount: uint256, liquidation_discount: uint256, amm: address): """ @notice Controller constructor deployed by the factory from blueprint @param collateral_token Token to use for collateral @param monetary_policy Address of monetary policy @param loan_discount Discount of the maximum loan size compare to get_x_down() value @param liquidation_discount Discount of the maximum loan size compare to get_x_down() for "bad liquidation" purposes @param amm AMM address (Already deployed from blueprint) """ FACTORY = Factory(msg.sender) 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 /= loan.rate_mul 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 = 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) -> int256: """ @notice Calculate the upper band number for the deposit to sit in to support the given debt. Reverts if requested debt is too high. @param collateral Amount of collateral (at its native precision) @param debt Amount of requested debt @param N Number of bands to deposit into @return Upper band n1 (n1 <= n2) to deposit into. Signed integer """ assert debt > 0, "No loan" n0: int256 = AMM.active_band() p_base: uint256 = AMM.p_oracle_up(n0) # x_effective = y / N * p_oracle_up(n1) * sqrt((A - 1) / A) * sum_{0..N-1}(((A-1) / A)**k) # === d_y_effective * p_oracle_up(n1) * sum(...) === y_effective * p_oracle_up(n1) # d_y_effective = y / N / sqrt(A / (A - 1)) y_effective: uint256 = self.get_y_effective(collateral * COLLATERAL_PRECISION, N, self.loan_discount) # p_oracle_up(n1) = base_price * ((A - 1) / A)**n1 # We borrow up until min band touches p_oracle, # or it touches non-empty bands which cannot be skipped. # We calculate required n1 for given (collateral, debt), # and if n1 corresponds to price_oracle being too high, or unreachable band # - we revert. # n1 is band number based on adiabatic trading, e.g. when p_oracle ~ p y_effective = 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) -> 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) @return Maximum amount of stablecoin to borrow """ # Calculation of maximum which can be borrowed. # It corresponds to a minimum between the amount corresponding to price_oracle # and the one given by the min reachable band. # # Given by p_oracle (perhaps needs to be multiplied by (A - 1) / A to account for mid-band effects) # x_max ~= y_effective * p_oracle # # Given by band number: # if n1 is the lowest empty band in the AMM # xmax ~= y_effective * amm.p_oracle_up(n1) # # When n1 -= 1: # p_oracle_up *= A / (A - 1) y_effective: uint256 = self.get_y_effective(collateral * COLLATERAL_PRECISION, N, self.loan_discount) x: uint256 = unsafe_sub(max(unsafe_div(y_effective * self.max_p_base(), 10**18), 1), 1) x = unsafe_div(x * (10**18 - 10**14), 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) -> uint256: """ @notice Minimal amount of collateral required to support debt @param debt The debt to support @param N Number of bands to deposit into @return Minimal collateral required """ # Add N**2 to account for precision loss in multiple bands, e.g. N / (y/N) = N**2 / y 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) + N * (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) -> int256: """ @notice Calculate the upper band number for the deposit to sit in to support the given debt. Reverts if requested debt is too high. @param collateral Amount of collateral (at its native precision) @param debt Amount of requested debt @param N Number of bands to deposit into @return Upper band n1 (n1 <= n2) to deposit into. Signed integer """ return self._calculate_debt_n1(collateral, debt, N) @internal def 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 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): assert self.loan[msg.sender].initial_debt == 0, "Loan already created" assert N > MIN_TICKS-1, "Need more ticks" assert N < MAX_TICKS+1, "Need less ticks" n1: int256 = self._calculate_debt_n1(collateral, debt, N) n2: int256 = n1 + convert(N - 1, int256) rate_mul: uint256 = AMM.get_rate_mul() self.loan[msg.sender] = Loan({initial_debt: debt, rate_mul: rate_mul}) liquidation_discount: uint256 = self.liquidation_discount self.liquidation_discounts[msg.sender] = liquidation_discount n_loans: uint256 = self.n_loans self.loans[n_loans] = msg.sender self.loan_ix[msg.sender] = n_loans self.n_loans = unsafe_add(n_loans, 1) 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(msg.sender, collateral, n1, n2) self.minted += debt if transfer_coins: self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral) self.transfer(BORROWED_TOKEN, msg.sender, debt) self._save_rate() log UserState(msg.sender, collateral, debt, n1, n2, liquidation_discount) log Borrow(msg.sender, collateral, debt) @external @nonreentrant('lock') def create_loan(collateral: uint256, debt: uint256, N: uint256): """ @notice Create loan @param collateral Amount of collateral to use @param debt Stablecoin debt to take @param N Number of bands to deposit into (to do autoliquidation-deliquidation), can be from MIN_TICKS to MAX_TICKS """ self._create_loan(collateral, debt, N, True) @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""): """ @notice Create loan but pass stablecoin to a callback first so that it can build leverage @param collateral Amount of collateral to use @param debt Stablecoin debt to take @param N Number of bands to deposit into (to do autoliquidation-deliquidation), can be from MIN_TICKS to MAX_TICKS @param callbacker Address of the callback contract @param callback_args Extra arguments for the callback (up to 5) such as min_amount etc """ # Before callback 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, msg.sender, 0, collateral, debt, callback_args, callback_bytes).collateral # After callback self._create_loan(collateral + more_collateral, debt, N, False) 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) 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): """ @notice Remove some collateral without repaying the debt @param collateral Amount of collateral to remove """ if collateral == 0: return self._add_collateral_borrow(collateral, 0, msg.sender, True) self.transferFrom(COLLATERAL_TOKEN, AMM.address, msg.sender, collateral) self._save_rate() @external @nonreentrant('lock') def borrow_more(collateral: uint256, debt: uint256): """ @notice Borrow more stablecoins while adding more collateral (not necessary) @param collateral Amount of collateral to add @param debt Amount of stablecoin debt to take """ if debt == 0: return self._add_collateral_borrow(collateral, debt, msg.sender, False) self.minted += debt self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral) self.transfer(BORROWED_TOKEN, msg.sender, 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""): """ @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 """ if debt == 0: return # 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, msg.sender, 0, collateral, debt, callback_args, callback_bytes).collateral # After callback self._add_collateral_borrow(collateral + more_collateral, debt, msg.sender, False) self.minted += debt self.transferFrom(COLLATERAL_TOKEN, msg.sender, AMM.address, collateral) self.transferFrom(COLLATERAL_TOKEN, callbacker, AMM.address, more_collateral) self._save_rate() @internal def _remove_from_list(_for: address): last_loan_ix: uint256 = self.n_loans - 1 loan_ix: uint256 = self.loan_ix[_for] assert self.loans[loan_ix] == _for # dev: should never fail but safety first self.loan_ix[_for] = 0 if loan_ix < last_loan_ix: # Need to replace last_loan: address = self.loans[last_loan_ix] self.loans[loan_ix] = last_loan self.loan_ix[last_loan] = loan_ix self.n_loans = last_loan_ix @external @nonreentrant('lock') def repay(_d_debt: uint256, _for: address = msg.sender, max_active_band: int256 = 2**255-1): """ @notice Repay debt (partially or fully) @param _d_debt The amount of debt to repay. If higher than the current debt - will do full repayment @param _for The user to repay the debt for @param max_active_band Don't allow active band to be higher than this (to prevent front-running the repay) """ if _d_debt == 0: return # Or repay all for MAX_UINT256 # Withdraw if debt become 0 debt: uint256 = 0 rate_mul: uint256 = 0 debt, rate_mul = self._debt(_for) assert debt > 0, "Loan doesn't exist" d_debt: uint256 = min(debt, _d_debt) debt = unsafe_sub(debt, d_debt) if debt == 0: # Allow to withdraw all assets even when underwater xy: uint256[2] = AMM.withdraw(_for, 10**18) if xy[0] > 0: # Only allow full repayment when underwater for the sender to do assert _for == msg.sender 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: uint256 = convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256) 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, size) n2: int256 = n1 + unsafe_sub(ns[1], ns[0]) AMM.deposit_range(_for, xy[1], n1, n2) if _for == msg.sender: # 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 _for != msg.sender: # Doesn't allow non-sender to repay in a way which ends with unhealthy state # full = False to make this condition non-manipulatable (and also cheaper on gas) assert self._health(_for, debt, False, liquidation_discount) > 0 # If we withdrew already - will burn less! 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""): """ @notice Repay loan but get a stablecoin for that from callback (to deleverage) @param callbacker Address of the callback contract @param callback_args Extra arguments for the callback (up to 5) such as min_amount etc """ # Before callback ns: int256[2] = AMM.read_user_tick_numbers(msg.sender) xy: uint256[2] = AMM.withdraw(msg.sender, 10**18) debt: uint256 = 0 rate_mul: uint256 = 0 debt, rate_mul = self._debt(msg.sender) 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, msg.sender, 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(msg.sender) # Transfer debt to self, everything else to sender 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, msg.sender, unsafe_sub(total_stablecoins, d_debt)) self.transferFrom(COLLATERAL_TOKEN, callbacker, msg.sender, cb.collateral) log UserState(msg.sender, 0, 0, 0, 0, 0) # Else - partial repayment -> deleverage, but only if we are not underwater else: size: uint256 = convert(unsafe_add(unsafe_sub(ns[1], ns[0]), 1), uint256) assert ns[0] > cb.active_band d_debt = cb.stablecoins # cb.stablecoins <= total_stablecoins < debt debt = unsafe_sub(debt, cb.stablecoins) # Not in liquidation - can move bands n1: int256 = self._calculate_debt_n1(cb.collateral, debt, size) n2: int256 = n1 + unsafe_sub(ns[1], ns[0]) AMM.deposit_range(msg.sender, cb.collateral, n1, n2) liquidation_discount: uint256 = self.liquidation_discount self.liquidation_discounts[msg.sender] = liquidation_discount 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(msg.sender, cb.collateral, debt, n1, n2, liquidation_discount) xy[1] -= cb.collateral # No need to check _health() because it's the sender # Common calls which we will do regardless of whether it's a full repay or not log Repay(msg.sender, xy[1], d_debt) self.redeemed += d_debt self.loan[msg.sender] = Loan({initial_debt: debt, rate_mul: rate_mul}) total_debt: uint256 = self._total_debt.initial_debt * rate_mul / self._total_debt.rate_mul self._total_debt.initial_debt = unsafe_sub(max(total_debt, d_debt), d_debt) self._total_debt.rate_mul = rate_mul 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) 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) # AMM has nonreentrant decorator @external def set_amm_admin_fee(fee: uint256): """ @notice Set AMM's admin fee @param fee New admin fee (not higher than MAX_ADMIN_FEE) """ assert msg.sender == FACTORY.admin() assert fee <= MAX_ADMIN_FEE, "High fee" AMM.set_admin_fee(fee) @nonreentrant('lock') @external def set_monetary_policy(monetary_policy: address): """ @notice Set monetary policy contract @param monetary_policy Address of the monetary policy contract """ assert msg.sender == FACTORY.admin() self.monetary_policy = MonetaryPolicy(monetary_policy) MonetaryPolicy(monetary_policy).rate_write() log SetMonetaryPolicy(monetary_policy) @nonreentrant('lock') @external def set_borrowing_discounts(loan_discount: uint256, liquidation_discount: uint256): """ @notice Set discounts at which we can borrow (defines max LTV) and where bad liquidation starts @param loan_discount Discount which defines LTV @param liquidation_discount Discount where bad liquidation starts """ assert msg.sender == FACTORY.admin() assert loan_discount > liquidation_discount assert liquidation_discount >= MIN_LIQUIDATION_DISCOUNT assert loan_discount <= MAX_LOAN_DISCOUNT self.liquidation_discount = liquidation_discount self.loan_discount = loan_discount log SetBorrowingDiscounts(loan_discount, liquidation_discount) @external @nonreentrant('lock') def set_callback(cb: address): """ @notice Set liquidity mining callback """ assert msg.sender == FACTORY.admin() AMM.set_callback(cb) @external @view def admin_fees() -> uint256: """ @notice Calculate the amount of fees obtained from the interest """ rate_mul: uint256 = AMM.get_rate_mul() loan: Loan = self._total_debt loan.initial_debt = loan.initial_debt * rate_mul / loan.rate_mul + 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() # AMM-based fees borrowed_fees: uint256 = AMM.admin_fees_x() collateral_fees: uint256 = AMM.admin_fees_y() self.transferFrom(BORROWED_TOKEN, AMM.address, _to, borrowed_fees) self.transferFrom(COLLATERAL_TOKEN, AMM.address, _to, collateral_fees) AMM.reset_admin_fees() # 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
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"name":"UserState","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral","type":"uint256","indexed":false},{"name":"debt","type":"uint256","indexed":false},{"name":"n1","type":"int256","indexed":false},{"name":"n2","type":"int256","indexed":false},{"name":"liquidation_discount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Borrow","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral_increase","type":"uint256","indexed":false},{"name":"loan_increase","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Repay","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral_decrease","type":"uint256","indexed":false},{"name":"loan_decrease","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveCollateral","inputs":[{"name":"user","type":"address","indexed":true},{"name":"collateral_decrease","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Liquidate","inputs":[{"name":"liquidator","type":"address","indexed":true},{"name":"user","type":"address","indexed":true},{"name":"collateral_received","type":"uint256","indexed":false},{"name":"stablecoin_received","type":"uint256","indexed":false},{"name":"debt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetMonetaryPolicy","inputs":[{"name":"monetary_policy","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetBorrowingDiscounts","inputs":[{"name":"loan_discount","type":"uint256","indexed":false},{"name":"liquidation_discount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"CollectFees","inputs":[{"name":"amount","type":"uint256","indexed":false},{"name":"new_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"collateral_token","type":"address"},{"name":"monetary_policy","type":"address"},{"name":"loan_discount","type":"uint256"},{"name":"liquidation_discount","type":"uint256"},{"name":"amm","type":"address"}],"outputs":[]},{"stateMutability":"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":"min_collateral","inputs":[{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calculate_debt_n1","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"N","type":"uint256"}],"outputs":[{"name":"","type":"int256"}]},{"stateMutability":"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_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":"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":"borrow_more","inputs":[{"name":"collateral","type":"uint256"},{"name":"debt","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[]"}],"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":"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":"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_amm_admin_fee","inputs":[{"name":"fee","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_monetary_policy","inputs":[{"name":"monetary_policy","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_borrowing_discounts","inputs":[{"name":"loan_discount","type":"uint256"},{"name":"liquidation_discount","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_callback","inputs":[{"name":"cb","type":"address"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"admin_fees","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"collect_fees","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"check_lock","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"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"}]}]
Contract Creation Code
6158235150346107cb576020615ec95f395f518060a01c6107cb57610160526020615ee95f395f518060a01c6107cb57610180526020615f495f395f518060a01c6107cb576101a052336156e3526101805168010000000000000008556020615f295f395f5168010000000000000009556020615f095f395f516801000000000000000a55670de0b6b3a76400006004556101a051615783526101a05163f446c1d06101e05260206101e060046101fc845afa6100be573d5f5f3e3d5ffd5b60203d106107cb576101e09050516101c0526101c0516157a35260016101c051036157c35260016101c051036101c051670de0b6b3a7640000810281670de0b6b3a76400008204186107cb5790500460a05261011b6101e061056d565b6101e0516157e3526157a351673782dace9d9000000467016345785d8a000081811867016345785d8a000083100218905061582352610160516101e0525f61020052610160516101f45733632621db2f610220526020610220600461023c845afa610188573d5f5f3e3d5ffd5b60203d106107cb57610220518060a01c6107cb57610260526102609050516101e0523363765337b6610220526020610220600461023c845afa6101cd573d5f5f3e3d5ffd5b60203d106107cb57610220518060a01c6107cb57610260526102609050516102005261023a565b3363e9cbd822610220526020610220600461023c845afa610217573d5f5f3e3d5ffd5b60203d106107cb57610220518060a01c6107cb5761026052610260905051610200525b6101e0516157035261020051615743526101e05163313ce567610220526020610220600461023c845afa610270573d5f5f3e3d5ffd5b60203d106107cb5761022090505180601203601281116107cb579050600a0a615723526102005163313ce567610220526020610220600461023c845afa6102b9573d5f5f3e3d5ffd5b60203d106107cb5761022090505180601203601281116107cb579050600a0a6157635260016101c051036101c0516ec097ce7bc90715b34b9f10000000008102816ec097ce7bc90715b34b9f10000000008204186107cb579050048060b571010000000000000000000000000000000000821061033d578160801c91508060401b90505b6901000000000000000000821061035b578160401c91508060201b90505b650100000000008210610375578160201c91508060101b90505b6301000000821061038d578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050615803526102005163095ea7b36102205233610240527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610260526020610220604461023c5f855af1610449573d5f5f3e3d5ffd5b60203d106107cb57610220518060011c6107cb576102805261028050506156e36107cf61000039615843610000f35b6040516060525f60805260405160801c1561049c5760405160801c60605260806080525b60605160401c156104ba5760605160401c6060526040608051016080525b60605160201c156104d85760605160201c6060526020608051016080525b60605160101c156104f65760605160101c6060526010608051016080525b60605160081c156105145760605160081c6060526008608051016080525b60605160041c156105325760605160041c6060526004608051016080525b60605160021c156105505760605160021c6060526002608051016080525b60605160011c15610565576001608051016080525b608051815250565b60a0518060ff1c6107cb5760c05260a051156107cb57606060a051604052610596610100610478565b610100518060ff1c6107cb570360e05260c05160e051609f035f81126107cb571b609f1c8060ff1c6107cb5760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b5f80fd5f3560e01c60026032820660011b61567f01601e395f51565b635457ff7b81186100535760243610341761567b576004358060a01c61567b5760405260026040516020525f5260405f205460605260206060f35b634189617d81186132205760243610341761567b5760206156e35f395f5163f851a440604052602060406004605c845afa610090573d5f5f3e3d5ffd5b60203d1061567b576040518060a01c61567b576080526080905051331861567b5760206158235f395f5160043511156100c9575f6100d3565b620f424060043510155b6101325760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b60206157835f395f51631aa02d59604052600435606052803b1561567b575f60406024605c5f855af1610167573d5f5f3e3d5ffd5b5000613220565b63e1ec3c6881186101a25760243610341761567b5760043567fffffffffffffffe811161567b576005015460405260206040f35b63a757320681186132205760443610341761567b575f5460021461567b57670de05bc096e9c00060206157235f395f5160043560206157635f395f51670de0b6b3a76400000280820281158383830414171561567b5790509050610207610220613c73565b61022051801561567b5780820490509050670de0b6b3a7640000810281670de0b6b3a764000082041861567b579050670de0b6b3a76400006040526024356060526801000000000000000a546080526102616102406136fa565b61024051801561567b57808204905090506024356024356107d0810181811061567b57905080820281158383830414171561567b579050905080820182811061567b5790509050600160206157235f395f510380820182811061567b579050905004670de0b6b3a7640000810281670de0b6b3a764000082041861567b57905004610260526020610260f3613220565b637128f3b881186103345760243610341761567b576004358060a01c61567b57604052680100000000000000046040516020525f5260405f205460605260206060f35b63d14ff5b681186103b15760243610341761567b575f5460021461567b5760025f55600435610362576103ab565b600435610300525f610320523361034052600161036052610381614762565b602061570360403960206157836060393360805260043560a0526103a3613eec565b6103ab613577565b60035f55005b63e2d8ebee81186132205760243610341761567b575f61020052612c7a56613220565b636cce39be8118613220573461567b57680100000000000000055460405260206040f3613220565b634f02c4208118613220573461567b57680100000000000000065460405260206040f3613220565b63e231bff08118610448573461567b57680100000000000000075460405260206040f35b63720fb25481186132205760643610341761567b575f5460021461567b576020606060046101603761047b6103006137f3565b610300f3613220565b63adfae4ce8118613220573461567b57680100000000000000085460405260206040f3613220565b63627d2b8381186104d0573461567b57680100000000000000095460405260206040f35b632621db2f8118613220573461567b57602061570360403960206040f3613220565b635449b9cb8118610516573461567b576801000000000000000a5460405260206040f35b63d0c581bf8118613220573461567b575f5460021461567b57600160405260206040f3613220565b63c45a01558118613220573461567b5760206156e360403960206040f3613220565b632a943945811861057e573461567b57602061578360403960206040f35b6324977ef381186107945760e43610341761567b5760843560040161271081351161567b5760208135018082617d203750505b6044358060a01c61567b57617c4052606435600401600581351161567b57803560208160051b018083617c60375050505f5460021461567b5760025f556024356105fa5761078e565b6020615743604039617c4051606052602435608052610617613f68565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a480525f61a4c05261a4c0805160208201209050617d2051617d402018610680577fe62214fe0000000000000000000000000000000000000000000000000000000061a480525b617c405160405261a48051606052336080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506106d761a4c0613fdc565b61a4c06040810190505161a4a05260043561a4a05180820182811061567b5790509050610300526024356103205233610340525f61036052610717614762565b680100000000000000065460243580820182811061567b57905090506801000000000000000655602061570360403933606052602061578360803960043560a052610760613eec565b6020615703604039617c4051606052602061578360803961a4a05160a052610786613eec565b61078e613577565b60035f55005b6322c7145381186132205760a43610341761567b57608435610340525b6004358060a01c61567b57610300526064358060011c61567b57610320525f5460021461567b5760206157835f395f5163b461100d6103a052610300516103c05260406103a060246103bc845afa61080b573d5f5f3e3d5ffd5b60403d1061567b576103a09050805161036052602081015161038052506103005160405261083a6103c0613604565b6103c0518060ff1c61567b576103a052610340516103c0525f6103e0526103a05115610898576002610300516020525f5260405f20548060ff1c61567b576103e0526001610360516103805103015f811261567b576103c0526108d5565b68010000000000000009548060ff1c61567b576103e0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610360525b606036610400376103a0516044358082018281125f83121861567b57905090506103a05260016103a051121561096a576011610460527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104805261046050610460518061048001601f825f031636823750506308c379a061042052602061044052601f19601f61046051011660440161043cfd5b60206157835f395f5163c16ef264610480526020610480600461049c845afa610995573d5f5f3e3d5ffd5b60203d1061567b5761048090505161046052610460516103605113610a3557610360516104005260206157835f395f516362ca4b1861048052610300516104a0526020610480602461049c845afa6109ef573d5f5f3e3d5ffd5b60203d1061567b5761048090505160206157635f395f51670de0b6b3a76400000280820281158383830414171561567b57905090508060ff1c61567b5761044052610b12565b60206157835f395f5163544fb5c161048052610300516104a0526040610480602461049c845afa610a68573d5f5f3e3d5ffd5b60403d1061567b576104809050602081019050518060ff1c61567b576024358082018281125f83121861567b579050905061042052610420515f811261567b57610160526103a0515f811261567b57610180526103c0516101a052610ace6104806137f3565b61048051610400526104205160206157235f395f518060ff1c61567b5780820281191515600160ff1b841415178215848484051417161561567b5790509050610420525b6103a05160206157635f395f518060ff1c61567b5780820281191515600160ff1b841415178215848484051417161561567b57905090506103a05260206157835f395f51632eb858e76104a052610400516104c05260206104a060246104bc845afa610b80573d5f5f3e3d5ffd5b60203d1061567b576104a09050518060ff1c61567b576104805261046051610360511315610c0357610420515f811261567b576040526103c0516060525f608052610bcc6104a06136fa565b6104a0518060ff1c61567b576104805180820281191515600160ff1b841415178215848484051417161561567b5790509050610440525b6103a05161044051056104a0526104a051670de0b6b3a76400006104a0516103e05180820281191515600160ff1b841415178215848484051417161561567b5790509050058082038281135f83121861567b5790509050670de0b6b3a7640000810381811361567b5790506104a0526103205115610d4a5761046051610400511315610d4a576104805160206157835f395f516386fc88d36104e05260206104e060046104fc845afa610cb8573d5f5f3e3d5ffd5b60203d1061567b576104e09050518060ff1c61567b5780828118828413021890509050610480518082038281135f83121861567b57905090506104c05260016104c05112610d4a576104a0516103a0516104c0516104205180820281191515600160ff1b841415178215848484051417161561567b5790509050058082018281125f83121861567b57905090506104a0525b60206104a0f3613220565b63765337b68118613220573461567b57602061574360403960206040f3613220565b6369c6804e8118610da2573461567b575f5460021461567b5760025f55610d9c613577565b60035f55005b63d9f11a648118613220573461567b57602060206157835f395f5163f2388acb604052602060406004605c845afa610ddc573d5f5f3e3d5ffd5b60203d1061567b5760409050f3613220565b639b6c56ec8118610e325760243610341761567b576004358060a01c61567b5760e0525f5460021461567b57602060e051604052610e2d610100613604565b610100f35b639a49719681186132205760443610341761567b575f610220526115d156613220565b63a21adb9e8118610e9b5760243610341761567b576004358060a01c61567b576040525f5460021461567b5760016040516020525f5260405f2054151560605260206060f35b63b4440df481186132205760643610341761567b576024358060a01c61567b5761030052604435610320525b5f5460021461567b5760025f55600435610ee05761151f565b6040366103403761030051604052610ef9610380613604565b6103808051610340526020810151610360525061034051610f79576012610380527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b61034051600435808281188284100218905090506103805261038051610340510361034052610340516110ec5760206157835f395f5163f3fef3a36103e0526103005161040052670de0b6b3a76400006104205260406103e060446103fc5f855af1610fe7573d5f5f3e3d5ffd5b60403d1061567b576103e0905080516103a05260208101516103c052506103a0511561103d5733610300511861567b5760206157436040396020615783606039610300516080526103a05160a05261103d613eec565b6103c0511561106c5760206157036040396020615783606039610300516080526103c05160a05261106c613eec565b610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366103e03760a06103e0a2610300517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d06103c0516103e052610380516104005260406103e0a26103005160405261145e614bb85661145e565b60206157835f395f5163c16ef2646103c05260206103c060046103dc845afa611117573d5f5f3e3d5ffd5b60203d1061567b576103c09050516103a052610320516103a0511361567b5760206157835f395f5163b461100d6104005261030051610420526040610400602461041c845afa611169573d5f5f3e3d5ffd5b60403d1061567b57610400905080516103c05260208101516103e0525060016103c0516103e05103015f811261567b57610400526002610300516020525f5260405f2054610420526103a0516103c0511361126b57610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6104405261034051610460526103c051610480526103e0516104a052610420516104c05260a0610440a2610300517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f6104405261038051610460526040610440a2611424565b60206157835f395f5163f3fef3a361048052610300516104a052670de0b6b3a76400006104c0526040610480604461049c5f855af16112ac573d5f5f3e3d5ffd5b60403d1061567b5761048090508051610440526020810151610460525061046051610160526103405161018052610400516101a0526112ec6104a06137f3565b6104a05161048052610480516103c0516103e051038082018281125f83121861567b57905090506104a05260206157835f395f5163ab047e006104c052610300516104e052610460516105005261048051610520526104a05161054052803b1561567b575f6104c060846104dc5f855af1611369573d5f5f3e3d5ffd5b5033610300511861139857680100000000000000095461042052610420516002610300516020525f5260405f20555b610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610460516104c052610340516104e05261048051610500526104a05161052052610420516105405260a06104c0a2610300517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f6104c052610380516104e05260406104c0a25b33610300511461145e57600161030051604052610340516060525f6080526104205160a052611454610440614c8c565b610440511261567b575b602061574360403933606052306080526103805160a05261147d613eec565b68010000000000000007546103805180820182811061567b579050905068010000000000000007556001610300516020525f5260405f20610340518155610360516001820155506003546103605180820281158383830414171561567b5790509050600454801561567b57808204905090506103a052610380516103a0516103805180828118828411021890509050036003556103605160045561151f613577565b60035f5500613220565b6331dc3ca88118613220573461567b5760206157835f395f5163095a0fc6606052602060606004607c845afa611561573d5f5f3e3d5ffd5b60203d1061567b57606090505160405260035460605260045460805260605160405180820281158383830414171561567b5790509050608051801561567b578082049050905060a052602060a0f3613220565b631cf1f947811861170f5760643610341761567b57604435610220525b5f5460021461567b5760043560206157235f395f5180820281158383830414171561567b57905090506040526024356060526801000000000000000a5460805261161c6102606136fa565b61026051610240526001670de0b6b3a76400006102405161163e610280613c73565b6102805180820281158383830414171561567b57905090500460018181186001831102189050036102605260206157635f395f51670de0b6b3a76400000261026051670de05bc096e9c000810281670de05bc096e9c00082041861567b57905004610260526102605160206157435f395f516370a0823161028052306102a0526020610280602461029c845afa6116d7573d5f5f3e3d5ffd5b60203d1061567b576102809050516102205180820182811061567b5790509050808281188284100218905090506102c05260206102c0f35b63bc61ea2381186132205760c43610341761567b575f617d20526117c756613220565b6323cfed0381186117705760643610341761567b575f5460021461567b5760025f55606060046103003760016103605261176a614348565b60035f55005b63152f65cb81186132205760643610341761567b575f617d2052611e0c56613220565b634ba96d468118613220576101043610341761567b5760a43560040161271081351161567b5760208135018082617d203750505b6064358060a01c61567b57617c4052608435600401600581351161567b57803560208160051b018083617c60375050505f5460021461567b5760025f556020615743604039617c4051606052602435608052611821613f68565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a480525f61a4c05261a4c0805160208201209050617d2051617d40201861188a577fe62214fe0000000000000000000000000000000000000000000000000000000061a480525b617c405160405261a48051606052336080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506118e161a4c0613fdc565b61a4c06040810190505161a4a05260043561a4a05180820182811061567b57905090506103005260406024610320375f6103605261191d614348565b602061570360403933606052602061578360803960043560a05261193f613eec565b6020615703604039617c4051606052602061578360803961a4a05160a052611965613eec565b60035f5500613220565b636f972f12811861198e5760243610341761567b573361056052611ae0565b6380e8f6ec81186119b45760243610341761567b576004356101e0525f610200526128a8565b63a5b4804a81186132205760243610341761567b5760206156e35f395f5163f851a440604052602060406004605c845afa6119f1573d5f5f3e3d5ffd5b60203d1061567b576040518060a01c61567b576080526080905051331861567b576706f05b59d3b200006004351115611a7f5760086040527f486967682066656500000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b60206157835f395f51633217902f604052600435606052803b1561567b575f60406024605c5f855af1611ab4573d5f5f3e3d5ffd5b5000613220565b6324049e5781186132205760443610341761567b576024358060a01c61567b57610560525b5f5460021461567b5760025f55600435611af957611b44565b600435610300525f6103205261056051610340525f61036052611b1a614762565b602061570360403933606052602061578360803960043560a052611b3c613eec565b611b44613577565b60035f5500613220565b63dd171e7c8118611c075760443610341761567b575f5460021461567b5760025f55602435611b7c57611c01565b604060046103003733610340525f61036052611b96614762565b680100000000000000065460243580820182811061567b57905090506801000000000000000655602061570360403933606052602061578360803960043560a052611bdf613eec565b602061574360403933606052602435608052611bf9613f68565b611c01613577565b60035f55005b63ec74d0a881186132205760243610341761567b576004358060a01c61567b5760e0525f5460021461567b5760206157835f395f5163544fb5c16101405260e051610160526040610140602461015c845afa611c65573d5f5f3e3d5ffd5b60403d1061567b5761014090508051610100526020810151610120525060206157835f395f5163b461100d6101805260e0516101a0526040610180602461019c845afa611cb4573d5f5f3e3d5ffd5b60403d1061567b57610180905080516101405260208101516101605250610120516101c052610100516101e05260e051604052611cf2610180613604565b61018051610200526001610140516101605103015f811261567b576102205260806101c0f3613220565b6336b7dbb781186132205760a43610341761567b575f617d20526105b156613220565b63371fd8e681186132205760243610341761567b5733610300527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61032052610ec756613220565b63acb7081581186132205760443610341761567b576024358060a01c61567b57610300527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61032052610ec756613220565b635794e89081186123c55760a43610341761567b5760443560040161271081351161567b5760208135018082617d203750505b6004358060a01c61567b57617c4052602435600401600581351161567b57803560208160051b018083617c60375050505f5460021461567b5760025f5560206157835f395f5163b461100d61a4c0523361a4e052604061a4c0602461a4dc845afa611e79573d5f5f3e3d5ffd5b60403d1061567b5761a4c09050805161a48052602081015161a4a0525060206157835f395f5163f3fef3a361a500523361a52052670de0b6b3a764000061a54052604061a500604461a51c5f855af1611ed4573d5f5f3e3d5ffd5b60403d1061567b5761a5009050805161a4c052602081015161a4e0525060403661a5003733604052611f0761a540613604565b61a540805161a50052602081015161a520525060206157036040396020615783606039617c405160805261a4e05160a052611f40613eec565b7e8ae1880000000000000000000000000000000000000000000000000000000061a540525f61a5805261a580805160208201209050617d2051617d402018611fa8577fef67dc740000000000000000000000000000000000000000000000000000000061a540525b617c405160405261a540516060523360805261a4c05160a05261a4e05160c05261a5005160e052617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa505061200961a5c0613fdc565b61a5c0805161a56052602081015161a58052604081015161a5a0525061a5805161a4c05180820182811061567b579050905061a5c05261a5c0511561567b575f61a5e05261a5005161a5c051101561220a57600161a4805161a4a05103015f811261567b5761a6005261a5605161a48051131561567b5761a5805161a5e05261a5805161a500510361a5005261a5a0516101605261a500516101805261a600516101a0526120b861a6406137f3565b61a6405161a6205261a6205161a4805161a4a051038082018281125f83121861567b579050905061a6405260206157835f395f5163ab047e0061a660523361a6805261a5a05161a6a05261a6205161a6c05261a6405161a6e052803b1561567b575f61a660608461a67c5f855af1612132573d5f5f3e3d5ffd5b50680100000000000000095461a6605261a660516002336020525f5260405f20556020615703604039617c4051606052602061578360803961a5a05160a052612179613eec565b6020615743604039617c40516060523060805261a5805160a05261219b613eec565b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261a5a05161a6805261a5005161a6a05261a6205161a6c05261a6405161a6e05261a6605161a7005260a061a680a261a4e05161a5a05180820382811161567b579050905061a4e0526122e8565b61a5005161a5e0525f61a5005233604052612223614bb8565b6020615743604039617c40516060523060805261a5805160a052612245613eec565b602061574360403960206157836060393060805261a4c05160a052612268613eec565b61a5e05161a5c05111156122965760206157436040393360605261a5e05161a5c05103608052612296613f68565b6020615703604039617c40516060523360805261a5a05160a0526122b8613eec565b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a6003760a061a600a25b337f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a4e05161a6005261a5e05161a62052604061a600a2680100000000000000075461a5e05180820182811061567b579050905068010000000000000007556001336020525f5260405f2061a50051815561a5205160018201555060035461a5205180820281158383830414171561567b5790509050600454801561567b578082049050905061a6005261a5e05161a6005161a5e051808281188284110218905090500360035561a520516004556123bf613577565b60035f55005b632a0c358681186132205760443610341761567b575f5460021461567b5760025f5560206156e35f395f5163f851a440604052602060406004605c845afa61240f573d5f5f3e3d5ffd5b60203d1061567b576040518060a01c61567b576080526080905051331861567b57602435600435111561567b57662386f26fc100006024351061567b576706f05b59d3b200006004351161567b5760243568010000000000000009556004356801000000000000000a557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a160035f5500613220565b630b8db68181186124cf5760843610341761567b575f610340526107b1565b631b1800e38118613220573461567b5760206157835f395f5163095a0fc6606052602060606004607c845afa612507573d5f5f3e3d5ffd5b60203d1061567b57606090505160405260035460605260045460805260605160405180820281158383830414171561567b5790509050608051801561567b5780820490509050680100000000000000075480820182811061567b5790509050606052680100000000000000065460a05260a05160605160a051808281188284110218905090500360c052602060c0f3613220565b63bcbaf487811861262e5760443610341761567b576004358060a01c61567b5761a6a0525f5460021461567b5760025f555f61a6c0523361a6a051146125f057600261a6a0516020525f5260405f205461a6c0525b61a6a051617c4052602435617c605261a6c051617c8052670de0b6b3a7640000617ca052604036617cc0375f617da052612628614ffc565b60035f55005b6381d2f1b781186132205760243610341761567b576004358060a01c61567b576040525f5460021461567b5760025f5560206156e35f395f5163f851a440606052602060606004607c845afa612686573d5f5f3e3d5ffd5b60203d1061567b576060518060a01c61567b5760a05260a0905051331861567b57604051680100000000000000085560405163e91f2f4c606052602060606004607c5f855af16126d8573d5f5f3e3d5ffd5b60203d1061567b57606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a160035f5500613220565b63fed9698c81186132205760c43610341761567b575f61a7a05261277156613220565b63d28952fc8118613220576101043610341761567b5760a43560040161271081351161567b576020813501808261a7a03750505b6004358060a01c61567b5761a6a0526064358060a01c61567b5761a6c052608435600401600581351161567b57803560208160051b01808361a6e0375050505f5460021461567b5760025f555f61cf00523361a6a051146127e157600261a6a0516020525f5260405f205461cf00525b61a6a051617c4052602435617c605261cf0051617c8052604435670de0b6b3a7640000818118670de0b6b3a7640000831002189050617ca05261a6c051617cc05261a6e05160208160051b0180617ce08261a6e060045afa505050602061a7a0510180617da08261a7a060045afa5050612859614ffc565b60035f5500613220565b631b25cdaf811861288a5760243610341761567b57670de0b6b3a764000061010052612b39565b6390f8667d81186132205760443610341761567b57604060046101e0375b5f5460021461567b576801000000000000000554610220526102005161024052610200516128d95761022051610240525b6101e051610260525f610280525f620f4240905b80620273a052610220516102605110156129115761024051620273a0511815612914565b60015b1561291e57612a89565b6102605167fffffffffffffffe811161567b5760050154620273c052620273c05160405261294e62027400613604565b6202740051620273e052620273c051604052620273e05160605260016080526002620273c0516020525f5260405f205460a05261298d62027420614c8c565b620274205162027400527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff620274005113612a695760206157835f395f5163544fb5c16202746052620273c051620274805260406202746060246202747c845afa6129fa573d5f5f3e3d5ffd5b60403d1061567b57620274609050805162027420526020810151620274405250610280516103e7811161567b5760a081026102a001620273c05181526202742051602082015262027440516040820152620273e051606082015262027400516080820152506001810161028052505b610260516001810181811061567b579050610260526001018181186128ed575b5050602080620273a05280620273a0015f6102805180835260a081025f826103e8811161567b578015612b0257905b60a08102602087010160a082026102a00180518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050600101818118612ab8575b50508201602001915050905081019050620273a0f3613220565b63546e040d81186132205760443610341761567b57602435610100525b6004358060a01c61567b5760e0525f5460021461567b575f610120523360e05114612b7257600260e0516020525f5260405f2054610120525b670de0b6b3a764000060206157835f395f5163544fb5c16101605260e051610180526040610160602461017c845afa612bad573d5f5f3e3d5ffd5b60403d1061567b576101609050516101005160405261012051606052612bd46101c0614f8f565b6101c05180820281158383830414171561567b57905090500461014052670de0b6b3a764000060e051604052612c0b610180613604565b610180516101005180820281158383830414171561567b579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f3613220565b638908ea8281186132205760443610341761567b576024358060011c61567b57610200525b6004358060a01c61567b576101e0525f5460021461567b5760206101e051610280526101e051604052612cae610220613604565b610220516102a052610200516102c05260026101e0516020525f5260405f20546102e052610280516040526102a0516060526102c0516080526102e05160a052612cf9610260614c8c565b610260f3613220565b627c98ab8118612d1d573461567b576040366101e0376128a8565b63cc1891c781186132205760243610341761567b576004358060a01c61567b576040525f5460021461567b5760025f5560206156e35f395f5163f851a440606052602060606004607c845afa612d75573d5f5f3e3d5ffd5b60203d1061567b576060518060a01c61567b5760a05260a0905051331861567b5760206157835f395f5163cc1891c7606052604051608052803b1561567b575f60606024607c5f855af1612dcb573d5f5f3e3d5ffd5b5060035f5500613220565b632c5089c381186132205760243610341761567b576004358060a01c61567b576040525f5460021461567b5760206157835f395f5163e8dd1ef1606052604051608052602060606024607c845afa612e30573d5f5f3e3d5ffd5b60203d1061567b576060518060011c61567b5760a05260a09050511561567b5760206157835f395f5163b461100d60a05260405160c052604060a0602460bc845afa612e7e573d5f5f3e3d5ffd5b60403d1061567b5760a09050805160605260208101516080525060206157835f395f51632eb858e760a05260605160c052602060a0602460bc845afa612ec6573d5f5f3e3d5ffd5b60203d1061567b5760a09050516101205260206157835f395f516324299b7a60e05260805161010052602060e0602460fc845afa612f06573d5f5f3e3d5ffd5b60203d1061567b5760e0905051610140526040610120f3613220565b631e0cfcef8118613220573461567b575f5460021461567b5760025f5560206156e35f395f5163cab4d3db610180526020610180600461019c845afa612f6a573d5f5f3e3d5ffd5b60203d1061567b57610180518060a01c61567b576101c0526101c09050516101605260206157835f395f5163d1fea7336101a05260206101a060046101bc845afa612fb7573d5f5f3e3d5ffd5b60203d1061567b576101a09050516101805260206157835f395f516389960ba76101c05260206101c060046101dc845afa612ff4573d5f5f3e3d5ffd5b60203d1061567b576101c09050516101a05260206157436040396020615783606039610160516080526101805160a05261302c613eec565b60206157036040396020615783606039610160516080526101a05160a052613052613eec565b60206157835f395f5163822fe5076101c052803b1561567b575f6101c060046101dc5f855af1613084573d5f5f3e3d5ffd5b5060206157835f395f5163095a0fc66101e05260206101e060046101fc845afa6130b0573d5f5f3e3d5ffd5b60203d1061567b576101e09050516101c0526003546101e052600454610200526101e0516101c05180820281158383830414171561567b579050905061020051801561567b57808204905090506101e0526101c051610200526101e0516003556102005160045561311f613577565b6101e051680100000000000000075480820182811061567b5790509050610220526801000000000000000654610240526102405161022051116131a3577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f5f610260526101e051610280526040610260a15f61026052602061026061321a5661321a565b61022051680100000000000000065561024051610220510361022052602061574360403961016051606052610220516080526131dd613f68565b7f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f61022051610260526101e051610280526040610260a160206102205b60035f55f35b5f5ffd5b6040516060525f60805260405160801c156132485760405160801c60605260806080525b60605160401c156132665760605160401c6060526040608051016080525b60605160201c156132845760605160201c6060526020608051016080525b60605160101c156132a25760605160101c6060526010608051016080525b60605160081c156132c05760605160081c6060526008608051016080525b60605160041c156132de5760605160041c6060526004608051016080525b60605160021c156132fc5760605160021c6060526002608051016080525b60605160011c15613311576001608051016080525b608051815250565b60a0518060ff1c61567b5760c05260a0511561567b57606060a051604052613342610100613224565b610100518060ff1c61567b570360e05260c05160e051609f035f811261567b571b609f1c8060ff1c61567b5760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b680100000000000000085463e91f2f4c606052602060606004607c5f855af16135a2573d5f5f3e3d5ffd5b60203d1061567b576060905051640a3c2abcef818118640a3c2abcef83100218905060405260206157835f395f5163d4387a99606052604051608052602060606024607c5f855af16135f6573d5f5f3e3d5ffd5b60203d1061567b5760605050565b60206157835f395f5163095a0fc6608052602060806004609c845afa61362c573d5f5f3e3d5ffd5b60203d1061567b57608090505160605260016040516020525f5260405f208054608052600181015460a05250608051613674575f81526060516020820152506136f8566136f8565b60805160605180820281158383830414171561567b579050905060c05260c05160a051801561567b5780820690509050156136d35760026801000000000000000554106136d35760c05160a05180820182811061567b579050905060c0525b60c05160a051801561567b578082049050905060c05260c05181526060516020820152505b565b604051608051606051604051046103e88181186103e8831102189050683635c9adc5dea000000480820182811061567b5790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a76400000380820281158383830414171561567b579050905060605160206158035f395f5102801561567b578082049050905060a05260a05160c052600160318101905b8060e05260605160e051186137a7576137e9565b60206157a35f395f5160a05160206157c35f395f5180820281158383830414171561567b57905090500460a05260a05160c0510160c052600101818118613793575b505060c051815250565b610180516138605760076101c0527f4e6f206c6f616e000000000000000000000000000000000000000000000000006101e0526101c0506101c051806101e001601f825f031636823750506308c379a06101805260206101a052601f19601f6101c051011660440161019cfd5b60206157835f395f51638f8654c56101e05260206101e060046101fc845afa61388b573d5f5f3e3d5ffd5b60203d1061567b576101e09050516101c05260206157835f395f51632eb858e7610200526101c051610220526020610200602461021c845afa6138d0573d5f5f3e3d5ffd5b60203d1061567b576102009050516101e0526101605160206157235f395f5180820281158383830414171561567b57905090506040526101a0516060526801000000000000000a546080526139266102206136fa565b61022051610200526101805160206157635f395f5180820281158383830414171561567b57905090506001810181811061567b579050610200516101e05180820281158383830414171561567b57905090500461020052610200516139ea57600e610220527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006102405261022050610220518061024001601f825f031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b6102005160a0526139fc610240613319565b61024051610220527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102205113613a555761022051600160206157e35f395f51038082038281135f83121861567b5790509050610220525b60206157e35f395f51610220510561022052610220516101a0518060ff1c61567b57806104000361040081135f83121861567b579050808281188284120218905090506101c0518082018281125f83121861567b5790509050610220526101c0516102205113613b865760206157835f395f5163ec65470661024052610220516001810381811361567b579050610260526020610240602461025c845afa613aff573d5f5f3e3d5ffd5b60203d1061567b57610240518060011c61567b5761028052610280905051613b8657600d6102a0527f4465627420746f6f2068696768000000000000000000000000000000000000006102c0526102a0506102a051806102c001601f825f031636823750506308c379a061026052602061028052601f19601f6102a051011660440161027cfd5b60206157835f395f516386fc88d3610280526020610280600461029c845afa613bb1573d5f5f3e3d5ffd5b60203d1061567b5761028090505160206157835f395f51632eb858e76102405261022051610260526020610240602461025c845afa613bf2573d5f5f3e3d5ffd5b60203d1061567b5761024090505110613c6a57600d6102c0527f4465627420746f6f2068696768000000000000000000000000000000000000006102e0526102c0506102c051806102e001601f825f031636823750506308c379a06102805260206102a052601f19601f6102c051011660440161029cfd5b61022051815250565b60206157835f395f516386fc88d3610180526020610180600461019c845afa613c9e573d5f5f3e3d5ffd5b60203d1061567b576101809050516101605260206157835f395f5163a7db79a56101a05260206101a060046101bc845afa613cdb573d5f5f3e3d5ffd5b60203d1061567b576101a0905051670de0b6b3a7640000810281670de0b6b3a764000082041861567b57905061016051801561567b578082049050905060a052613d266101e0613319565b6101e051610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113613d89576101805160206157e35f395f516001810381811361567b5790508082038281135f83121861567b5790509050610180525b60206157e35f395f5161018051056005810181811261567b5790506101805260206157835f395f5163c16ef2646101c05260206101c060046101dc845afa613dd3573d5f5f3e3d5ffd5b60203d1061567b576101c09050516101a052610180516101a0516001810181811261567b579050808281188284130218905090506101805260206157835f395f51632eb858e76101e052610180516102005260206101e060246101fc845afa613e3e573d5f5f3e3d5ffd5b60203d1061567b576101e09050516101c0525f610401905b806101e052610180516001810381811361567b579050610180526101a0516101805113613e8257613ee0565b6101c0516102005260206157c35f395f516101c05160206157a35f395f5180820281158383830414171561567b5790509050046101c052610160516101c0511115613ed557610200518352505050613eea565b600101818118613e56575b50506101c0518152505b565b60a05115613f66576040516323b872dd60c05260605160e0526080516101005260a05161012052602060c0606460dc5f855af1613f2b573d5f5f3e3d5ffd5b3d613f4257803b1561567b57600161014052613f5a565b60203d1061567b5760c0518060011c61567b57610140525b6101409050511561567b575b565b60805115613fda5760405163a9059cbb60a05260605160c05260805160e052602060a0604460bc5f855af1613f9f573d5f5f3e3d5ffd5b3d613fb657803b1561567b57600161010052613fce565b60203d1061567b5760a0518060011c61567b57610100525b6101009050511561567b575b565b60206157035f395f516040511461567b576060366129003760206157835f395f51638f8654c5612960526020612960600461297c845afa61401f573d5f5f3e3d5ffd5b60203d1061567b576129609050516129005260206157835f395f5163ebcb006761298052612900516129a0526020612980602461299c845afa614064573d5f5f3e3d5ffd5b60203d1061567b576129809050516129605260206157835f395f516331f7e3066129a052612900516129c05260206129a060246129bc845afa6140a9573d5f5f3e3d5ffd5b60203d1061567b576129a0905051612980526040515a5f60605181615300015260048101905060c0608051612a205260a051612a405260c051612a605260e051612a805280612aa05280612a20015f610100518083528060051b5f826005811161567b57801561413357905b8060051b61012001518160051b602088010152600101818118614115575b5050820160200191505090508101905080612ac05280612a200160206101c051018082826101c060045afa50508051806020830101601f825f03163682375050601f19601f82516020010116905081019050612a0052612a0080516020820183615300018281848460045afa50505080830192505050806152e0526152e050506040617c006152e0516153005f8686f1905090506141d3573d5f5f3e3d5ffd5b3d604081183d6040100218617be052617be06020815101806129a0828460045afa5050506129a05160201161567b576129c051612a20526020612a0052612a006020810151815160200360031b1c9050612920526129a05160401161567b576129e051612a20526020612a0052612a006020810151815160200360031b1c90506129405260206157835f395f51638f8654c5612a00526020612a006004612a1c845afa614282573d5f5f3e3d5ffd5b60203d1061567b57612a00905051612900511861567b5760206157835f395f5163ebcb0067612a005261290051612a20526020612a006024612a1c845afa6142cc573d5f5f3e3d5ffd5b60203d1061567b57612a00905051612960511861567b5760206157835f395f516331f7e306612a005261290051612a20526020612a006024612a1c845afa614316573d5f5f3e3d5ffd5b60203d1061567b57612a00905051612980511861567b5761290051815261292051602082015261294051604082015250565b6001336020525f5260405f2054156143bf576014610380527f4c6f616e20616c726561647920637265617465640000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b600461034051101561443057600f610380527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b60326103405111156144a157600f610380527f4e656564206c657373207469636b7300000000000000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b61030051610160526103205161018052610340516101a0526144c46103a06137f3565b6103a0516103805261038051610340516001810381811161567b5790508060ff1c61567b578082018281125f83121861567b57905090506103a05260206157835f395f5163095a0fc66103e05260206103e060046103fc845afa61452a573d5f5f3e3d5ffd5b60203d1061567b576103e09050516103c0526001336020525f5260405f206103205181556103c05160018201555068010000000000000009546103e0526103e0516002336020525f5260405f2055680100000000000000055461040052336104005167fffffffffffffffe811161567b57600501556104005168010000000000000004336020525f5260405f20556001610400510168010000000000000005556003546103c05180820281158383830414171561567b5790509050600454801561567b57808204905090506103205180820182811061567b57905090506003556103c05160045560206157835f395f5163ab047e00610420523361044052610300516104605261038051610480526103a0516104a052803b1561567b575f610420608461043c5f855af1614660573d5f5f3e3d5ffd5b5068010000000000000006546103205180820182811061567b5790509050680100000000000000065561036051156146d05760206157036040393360605260206157836080396103005160a0526146b5613eec565b602061574360403933606052610320516080526146d0613f68565b6146d8613577565b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26103005161042052610320516104405261038051610460526103a051610480526103e0516104a05260a0610420a2337fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610300516104205261032051610440526040610420a2565b604036610380376103405160405261477b6103c0613604565b6103c080516103805260208101516103a05250610380516147fb5760126103c0527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b610380516103205180820182811061567b57905090506103805260206157835f395f5163b461100d6104005261034051610420526040610400602461041c845afa614848573d5f5f3e3d5ffd5b60403d1061567b57610400905080516103c05260208101516103e0525060016103c0516103e05103015f811261567b576104005260206157835f395f5163f3fef3a3610460526103405161048052670de0b6b3a76400006104a0526040610460604461047c5f855af16148bd573d5f5f3e3d5ffd5b60403d1061567b57610460905080516104205260208101516104405250610420511561494857601a610460527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104805261046050610460518061048001601f825f031636823750506308c379a061042052602061044052601f19601f61046051011660440161043cfd5b6103605161496f57610440516103005180820182811061567b57905090506104405261498a565b610440516103005180820382811161567b5790509050610440525b61044051610160526103805161018052610400516101a0526149ad6104806137f3565b6104805161046052610460516103c0516103e051038082018281125f83121861567b57905090506104805260206157835f395f5163ab047e006104a052610340516104c052610440516104e05261046051610500526104805161052052803b1561567b575f6104a060846104bc5f855af1614a2a573d5f5f3e3d5ffd5b506001610340516020525f5260405f206103805181556103a0516001820155505f6104a052336103405118614a815768010000000000000009546104a0526104a0516002610340516020525f5260405f2055614a96565b6002610340516020525f5260405f20546104a0525b6103205115614ae7576003546103a05180820281158383830414171561567b5790509050600454801561567b57808204905090506103205180820182811061567b57905090506003556103a0516004555b61036051614b2f57610340517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610300516104c052610320516104e05260406104c0a2614b63565b610340517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb54610300516104c05260206104c0a25b610340517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610440516104c052610380516104e052610460516105005261048051610520526104a0516105405260a06104c0a2565b68010000000000000005546001810381811161567b579050606052680100000000000000046040516020525f5260405f205460805260405160805167fffffffffffffffe811161567b57600501541861567b575f680100000000000000046040516020525f5260405f20556060516080511015614c7c5760605167fffffffffffffffe811161567b576005015460a05260a05160805167fffffffffffffffe811161567b57600501556080516801000000000000000460a0516020525f5260405f20555b6060516801000000000000000555565b606051614cef57601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f825f031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60a0518060ff1c61567b5780670de0b6b3a764000003670de0b6b3a764000081135f83121861567b57905060c0526060518060ff1c61567b5760206157835f395f516362ca4b1860e05260405161010052602060e0602460fc845afa614d57573d5f5f3e3d5ffd5b60203d1061567b5760e09050518060ff1c61567b5760c05180820281191515600160ff1b841415178215848484051417161561567b579050905005670de0b6b3a7640000810381811361567b57905060c05260805115614f875760206157835f395f5163b461100d61010052604051610120526040610100602461011c845afa614de3573d5f5f3e3d5ffd5b60403d1061567b5761010090505160e05260206157835f395f51638f8654c5610100526020610100600461011c845afa614e1f573d5f5f3e3d5ffd5b60203d1061567b5761010090505160e0511315614f875760206157835f395f516386fc88d3610160526020610160600461017c845afa614e61573d5f5f3e3d5ffd5b60203d1061567b576101609050516101405260206157835f395f51632eb858e76101805260e0516101a0526020610180602461019c845afa614ea5573d5f5f3e3d5ffd5b60203d1061567b576101809050516101605261016051610140511115614f875760c05160605160206157635f395f5180820281158383830414171561567b579050905061016051610140510360206157835f395f5163544fb5c1610180526040516101a0526040610180602461019c845afa614f23573d5f5f3e3d5ffd5b60403d1061567b5761018090506020810190505180820281158383830414171561567b579050905060206157235f395f5180820281158383830414171561567b5790509050048060ff1c61567b578082018281125f83121861567b579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff60405111614ff457606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b60403661a4e037617c405160405261501561a520613604565b61a520805161a4e052602081015161a5005250617c8051156150e6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff617c405160405261a4e0516060526001608052617c805160a05261507761a520614c8c565b61a5205113156150e657600f61a540527f4e6f7420656e6f7567682072656b74000000000000000000000000000000000061a5605261a5405061a540518061a56001601f825f031636823750506308c379a061a50052602061a52052601f19601f61a54051011660440161a51cfd5b61a4e05161a52052670de0b6b3a764000061a4e051617ca05180820281158383830414171561567b57905090500461a4e05261a4e0511561567b5761a4e05161a520510361a5205260206157835f395f5163f3fef3a361a5a052617c405161a5c052617ca051604052617c805160605261516161a580614f8f565b61a5805161a5e052604061a5a0604461a5bc5f855af1615183573d5f5f3e3d5ffd5b60403d1061567b5761a5a09050805161a54052602081015161a5605250617c605161a54051101561521357600861a580527f536c69707061676500000000000000000000000000000000000000000000000061a5a05261a5805061a580518061a5a001601f825f031636823750506308c379a061a54052602061a56052601f19601f61a58051011660440161a55cfd5b61a5405161a4e0518082811882841002189050905061a58052602061574360403960206157836060393060805261a5805160a05261524f613eec565b61a5405161a4e051116152b957602061570360403960206157836060393360805261a5605160a05261527f613eec565b61a4e05161a54051111561550f57602061574360403960206157836060393360805261a4e05161a540510360a05261550f613eec5661550f565b61a5405161a4e0510361a5a052617cc05161531457602061570360403960206157836060393360805261a5605160a0526152f1613eec565b6020615743604039336060523060805261a5a05160a05261550f613eec5661550f565b60206157036040396020615783606039617cc05160805261a5605160a05261533a613eec565b7f750abc510000000000000000000000000000000000000000000000000000000061a5c0525f61a6005261a600805160208201209050617da051617dc020186153a3577f4ea696bb0000000000000000000000000000000000000000000000000000000061a5c0525b617cc05160405261a5c051606052617c405160805261a5405160a05261a5605160c05261a4e05160e052617ce05160208160051b018061010082617ce060045afa5050506020617da05101806101c082617da060045afa505061540761a640613fdc565b61a640805161a5e052602081015161a60052604081015161a620525061a5a05161a60051101561549657601361a640527f6e6f7420656e6f7567682070726f63656564730000000000000000000000000061a6605261a6405061a640518061a66001601f825f031636823750506308c379a061a60052602061a62052601f19601f61a64051011660440161a61cfd5b61a5a05161a6005111156154cb576020615743604039617cc0516060523360805261a5a05161a600510360a0526154cb613eec565b6020615743604039617cc0516060523060805261a5a05160a0526154ed613eec565b6020615703604039617cc0516060523360805261a6205160a05261550f613eec565b680100000000000000075461a4e05180820182811061567b579050905068010000000000000007556001617c40516020525f5260405f2061a52051815561a50051600182015550617c40517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5605161a5a05261a4e05161a5c052604061a5a0a2617c4051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d061a5605161a5a05261a5405161a5c05261a4e05161a5e052606061a5a0a361a5205161561e57617c40517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a5a03760a061a5a0a2617c405160405261561e614bb8565b60035461a5005180820281158383830414171561567b5790509050600454801561567b578082049050905061a5a05261a4e05161a5a05161a4e051808281188284110218905090500360035561a50051600455615679613577565b565b5f80fd0e552dd63220259b0d771abb1dd904ac17931d87016e0560196f2f222c5532200424053e1b4e28630dee32201d3f24b03220322003d43220322000183220322015293220322004f2273d17320d5515b402f12b1c0484322003fc3220271a2d0232201d1c841956e3811864190160a16576797065728300030a001700000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e478df674e25b2724b1ac9bcaba644943da359f000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000f8b0a10e470008000000000000000000000000cd28cf8f7755f03967d27e128b38022b63919836
Deployed Bytecode
0x5f3560e01c60026032820660011b61567f01601e395f51565b635457ff7b81186100535760243610341761567b576004358060a01c61567b5760405260026040516020525f5260405f205460605260206060f35b634189617d81186132205760243610341761567b5760206156e35f395f5163f851a440604052602060406004605c845afa610090573d5f5f3e3d5ffd5b60203d1061567b576040518060a01c61567b576080526080905051331861567b5760206158235f395f5160043511156100c9575f6100d3565b620f424060043510155b6101325760036040527f466565000000000000000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b60206157835f395f51631aa02d59604052600435606052803b1561567b575f60406024605c5f855af1610167573d5f5f3e3d5ffd5b5000613220565b63e1ec3c6881186101a25760243610341761567b5760043567fffffffffffffffe811161567b576005015460405260206040f35b63a757320681186132205760443610341761567b575f5460021461567b57670de05bc096e9c00060206157235f395f5160043560206157635f395f51670de0b6b3a76400000280820281158383830414171561567b5790509050610207610220613c73565b61022051801561567b5780820490509050670de0b6b3a7640000810281670de0b6b3a764000082041861567b579050670de0b6b3a76400006040526024356060526801000000000000000a546080526102616102406136fa565b61024051801561567b57808204905090506024356024356107d0810181811061567b57905080820281158383830414171561567b579050905080820182811061567b5790509050600160206157235f395f510380820182811061567b579050905004670de0b6b3a7640000810281670de0b6b3a764000082041861567b57905004610260526020610260f3613220565b637128f3b881186103345760243610341761567b576004358060a01c61567b57604052680100000000000000046040516020525f5260405f205460605260206060f35b63d14ff5b681186103b15760243610341761567b575f5460021461567b5760025f55600435610362576103ab565b600435610300525f610320523361034052600161036052610381614762565b602061570360403960206157836060393360805260043560a0526103a3613eec565b6103ab613577565b60035f55005b63e2d8ebee81186132205760243610341761567b575f61020052612c7a56613220565b636cce39be8118613220573461567b57680100000000000000055460405260206040f3613220565b634f02c4208118613220573461567b57680100000000000000065460405260206040f3613220565b63e231bff08118610448573461567b57680100000000000000075460405260206040f35b63720fb25481186132205760643610341761567b575f5460021461567b576020606060046101603761047b6103006137f3565b610300f3613220565b63adfae4ce8118613220573461567b57680100000000000000085460405260206040f3613220565b63627d2b8381186104d0573461567b57680100000000000000095460405260206040f35b632621db2f8118613220573461567b57602061570360403960206040f3613220565b635449b9cb8118610516573461567b576801000000000000000a5460405260206040f35b63d0c581bf8118613220573461567b575f5460021461567b57600160405260206040f3613220565b63c45a01558118613220573461567b5760206156e360403960206040f3613220565b632a943945811861057e573461567b57602061578360403960206040f35b6324977ef381186107945760e43610341761567b5760843560040161271081351161567b5760208135018082617d203750505b6044358060a01c61567b57617c4052606435600401600581351161567b57803560208160051b018083617c60375050505f5460021461567b5760025f556024356105fa5761078e565b6020615743604039617c4051606052602435608052610617613f68565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a480525f61a4c05261a4c0805160208201209050617d2051617d402018610680577fe62214fe0000000000000000000000000000000000000000000000000000000061a480525b617c405160405261a48051606052336080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506106d761a4c0613fdc565b61a4c06040810190505161a4a05260043561a4a05180820182811061567b5790509050610300526024356103205233610340525f61036052610717614762565b680100000000000000065460243580820182811061567b57905090506801000000000000000655602061570360403933606052602061578360803960043560a052610760613eec565b6020615703604039617c4051606052602061578360803961a4a05160a052610786613eec565b61078e613577565b60035f55005b6322c7145381186132205760a43610341761567b57608435610340525b6004358060a01c61567b57610300526064358060011c61567b57610320525f5460021461567b5760206157835f395f5163b461100d6103a052610300516103c05260406103a060246103bc845afa61080b573d5f5f3e3d5ffd5b60403d1061567b576103a09050805161036052602081015161038052506103005160405261083a6103c0613604565b6103c0518060ff1c61567b576103a052610340516103c0525f6103e0526103a05115610898576002610300516020525f5260405f20548060ff1c61567b576103e0526001610360516103805103015f811261567b576103c0526108d5565b68010000000000000009548060ff1c61567b576103e0527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610360525b606036610400376103a0516044358082018281125f83121861567b57905090506103a05260016103a051121561096a576011610460527f4e6f6e2d706f73697469766520646562740000000000000000000000000000006104805261046050610460518061048001601f825f031636823750506308c379a061042052602061044052601f19601f61046051011660440161043cfd5b60206157835f395f5163c16ef264610480526020610480600461049c845afa610995573d5f5f3e3d5ffd5b60203d1061567b5761048090505161046052610460516103605113610a3557610360516104005260206157835f395f516362ca4b1861048052610300516104a0526020610480602461049c845afa6109ef573d5f5f3e3d5ffd5b60203d1061567b5761048090505160206157635f395f51670de0b6b3a76400000280820281158383830414171561567b57905090508060ff1c61567b5761044052610b12565b60206157835f395f5163544fb5c161048052610300516104a0526040610480602461049c845afa610a68573d5f5f3e3d5ffd5b60403d1061567b576104809050602081019050518060ff1c61567b576024358082018281125f83121861567b579050905061042052610420515f811261567b57610160526103a0515f811261567b57610180526103c0516101a052610ace6104806137f3565b61048051610400526104205160206157235f395f518060ff1c61567b5780820281191515600160ff1b841415178215848484051417161561567b5790509050610420525b6103a05160206157635f395f518060ff1c61567b5780820281191515600160ff1b841415178215848484051417161561567b57905090506103a05260206157835f395f51632eb858e76104a052610400516104c05260206104a060246104bc845afa610b80573d5f5f3e3d5ffd5b60203d1061567b576104a09050518060ff1c61567b576104805261046051610360511315610c0357610420515f811261567b576040526103c0516060525f608052610bcc6104a06136fa565b6104a0518060ff1c61567b576104805180820281191515600160ff1b841415178215848484051417161561567b5790509050610440525b6103a05161044051056104a0526104a051670de0b6b3a76400006104a0516103e05180820281191515600160ff1b841415178215848484051417161561567b5790509050058082038281135f83121861567b5790509050670de0b6b3a7640000810381811361567b5790506104a0526103205115610d4a5761046051610400511315610d4a576104805160206157835f395f516386fc88d36104e05260206104e060046104fc845afa610cb8573d5f5f3e3d5ffd5b60203d1061567b576104e09050518060ff1c61567b5780828118828413021890509050610480518082038281135f83121861567b57905090506104c05260016104c05112610d4a576104a0516103a0516104c0516104205180820281191515600160ff1b841415178215848484051417161561567b5790509050058082018281125f83121861567b57905090506104a0525b60206104a0f3613220565b63765337b68118613220573461567b57602061574360403960206040f3613220565b6369c6804e8118610da2573461567b575f5460021461567b5760025f55610d9c613577565b60035f55005b63d9f11a648118613220573461567b57602060206157835f395f5163f2388acb604052602060406004605c845afa610ddc573d5f5f3e3d5ffd5b60203d1061567b5760409050f3613220565b639b6c56ec8118610e325760243610341761567b576004358060a01c61567b5760e0525f5460021461567b57602060e051604052610e2d610100613604565b610100f35b639a49719681186132205760443610341761567b575f610220526115d156613220565b63a21adb9e8118610e9b5760243610341761567b576004358060a01c61567b576040525f5460021461567b5760016040516020525f5260405f2054151560605260206060f35b63b4440df481186132205760643610341761567b576024358060a01c61567b5761030052604435610320525b5f5460021461567b5760025f55600435610ee05761151f565b6040366103403761030051604052610ef9610380613604565b6103808051610340526020810151610360525061034051610f79576012610380527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b61034051600435808281188284100218905090506103805261038051610340510361034052610340516110ec5760206157835f395f5163f3fef3a36103e0526103005161040052670de0b6b3a76400006104205260406103e060446103fc5f855af1610fe7573d5f5f3e3d5ffd5b60403d1061567b576103e0905080516103a05260208101516103c052506103a0511561103d5733610300511861567b5760206157436040396020615783606039610300516080526103a05160a05261103d613eec565b6103c0511561106c5760206157036040396020615783606039610300516080526103c05160a05261106c613eec565b610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a0366103e03760a06103e0a2610300517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d06103c0516103e052610380516104005260406103e0a26103005160405261145e614bb85661145e565b60206157835f395f5163c16ef2646103c05260206103c060046103dc845afa611117573d5f5f3e3d5ffd5b60203d1061567b576103c09050516103a052610320516103a0511361567b5760206157835f395f5163b461100d6104005261030051610420526040610400602461041c845afa611169573d5f5f3e3d5ffd5b60403d1061567b57610400905080516103c05260208101516103e0525060016103c0516103e05103015f811261567b57610400526002610300516020525f5260405f2054610420526103a0516103c0511361126b57610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6104405261034051610460526103c051610480526103e0516104a052610420516104c05260a0610440a2610300517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f6104405261038051610460526040610440a2611424565b60206157835f395f5163f3fef3a361048052610300516104a052670de0b6b3a76400006104c0526040610480604461049c5f855af16112ac573d5f5f3e3d5ffd5b60403d1061567b5761048090508051610440526020810151610460525061046051610160526103405161018052610400516101a0526112ec6104a06137f3565b6104a05161048052610480516103c0516103e051038082018281125f83121861567b57905090506104a05260206157835f395f5163ab047e006104c052610300516104e052610460516105005261048051610520526104a05161054052803b1561567b575f6104c060846104dc5f855af1611369573d5f5f3e3d5ffd5b5033610300511861139857680100000000000000095461042052610420516002610300516020525f5260405f20555b610300517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610460516104c052610340516104e05261048051610500526104a05161052052610420516105405260a06104c0a2610300517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d05f6104c052610380516104e05260406104c0a25b33610300511461145e57600161030051604052610340516060525f6080526104205160a052611454610440614c8c565b610440511261567b575b602061574360403933606052306080526103805160a05261147d613eec565b68010000000000000007546103805180820182811061567b579050905068010000000000000007556001610300516020525f5260405f20610340518155610360516001820155506003546103605180820281158383830414171561567b5790509050600454801561567b57808204905090506103a052610380516103a0516103805180828118828411021890509050036003556103605160045561151f613577565b60035f5500613220565b6331dc3ca88118613220573461567b5760206157835f395f5163095a0fc6606052602060606004607c845afa611561573d5f5f3e3d5ffd5b60203d1061567b57606090505160405260035460605260045460805260605160405180820281158383830414171561567b5790509050608051801561567b578082049050905060a052602060a0f3613220565b631cf1f947811861170f5760643610341761567b57604435610220525b5f5460021461567b5760043560206157235f395f5180820281158383830414171561567b57905090506040526024356060526801000000000000000a5460805261161c6102606136fa565b61026051610240526001670de0b6b3a76400006102405161163e610280613c73565b6102805180820281158383830414171561567b57905090500460018181186001831102189050036102605260206157635f395f51670de0b6b3a76400000261026051670de05bc096e9c000810281670de05bc096e9c00082041861567b57905004610260526102605160206157435f395f516370a0823161028052306102a0526020610280602461029c845afa6116d7573d5f5f3e3d5ffd5b60203d1061567b576102809050516102205180820182811061567b5790509050808281188284100218905090506102c05260206102c0f35b63bc61ea2381186132205760c43610341761567b575f617d20526117c756613220565b6323cfed0381186117705760643610341761567b575f5460021461567b5760025f55606060046103003760016103605261176a614348565b60035f55005b63152f65cb81186132205760643610341761567b575f617d2052611e0c56613220565b634ba96d468118613220576101043610341761567b5760a43560040161271081351161567b5760208135018082617d203750505b6064358060a01c61567b57617c4052608435600401600581351161567b57803560208160051b018083617c60375050505f5460021461567b5760025f556020615743604039617c4051606052602435608052611821613f68565b7f4cb0662c0000000000000000000000000000000000000000000000000000000061a480525f61a4c05261a4c0805160208201209050617d2051617d40201861188a577fe62214fe0000000000000000000000000000000000000000000000000000000061a480525b617c405160405261a48051606052336080525f60a0526040600460c037617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa50506118e161a4c0613fdc565b61a4c06040810190505161a4a05260043561a4a05180820182811061567b57905090506103005260406024610320375f6103605261191d614348565b602061570360403933606052602061578360803960043560a05261193f613eec565b6020615703604039617c4051606052602061578360803961a4a05160a052611965613eec565b60035f5500613220565b636f972f12811861198e5760243610341761567b573361056052611ae0565b6380e8f6ec81186119b45760243610341761567b576004356101e0525f610200526128a8565b63a5b4804a81186132205760243610341761567b5760206156e35f395f5163f851a440604052602060406004605c845afa6119f1573d5f5f3e3d5ffd5b60203d1061567b576040518060a01c61567b576080526080905051331861567b576706f05b59d3b200006004351115611a7f5760086040527f486967682066656500000000000000000000000000000000000000000000000060605260405060405180606001601f825f031636823750506308c379a05f526020602052601f19601f6040510116604401601cfd5b60206157835f395f51633217902f604052600435606052803b1561567b575f60406024605c5f855af1611ab4573d5f5f3e3d5ffd5b5000613220565b6324049e5781186132205760443610341761567b576024358060a01c61567b57610560525b5f5460021461567b5760025f55600435611af957611b44565b600435610300525f6103205261056051610340525f61036052611b1a614762565b602061570360403933606052602061578360803960043560a052611b3c613eec565b611b44613577565b60035f5500613220565b63dd171e7c8118611c075760443610341761567b575f5460021461567b5760025f55602435611b7c57611c01565b604060046103003733610340525f61036052611b96614762565b680100000000000000065460243580820182811061567b57905090506801000000000000000655602061570360403933606052602061578360803960043560a052611bdf613eec565b602061574360403933606052602435608052611bf9613f68565b611c01613577565b60035f55005b63ec74d0a881186132205760243610341761567b576004358060a01c61567b5760e0525f5460021461567b5760206157835f395f5163544fb5c16101405260e051610160526040610140602461015c845afa611c65573d5f5f3e3d5ffd5b60403d1061567b5761014090508051610100526020810151610120525060206157835f395f5163b461100d6101805260e0516101a0526040610180602461019c845afa611cb4573d5f5f3e3d5ffd5b60403d1061567b57610180905080516101405260208101516101605250610120516101c052610100516101e05260e051604052611cf2610180613604565b61018051610200526001610140516101605103015f811261567b576102205260806101c0f3613220565b6336b7dbb781186132205760a43610341761567b575f617d20526105b156613220565b63371fd8e681186132205760243610341761567b5733610300527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61032052610ec756613220565b63acb7081581186132205760443610341761567b576024358060a01c61567b57610300527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61032052610ec756613220565b635794e89081186123c55760a43610341761567b5760443560040161271081351161567b5760208135018082617d203750505b6004358060a01c61567b57617c4052602435600401600581351161567b57803560208160051b018083617c60375050505f5460021461567b5760025f5560206157835f395f5163b461100d61a4c0523361a4e052604061a4c0602461a4dc845afa611e79573d5f5f3e3d5ffd5b60403d1061567b5761a4c09050805161a48052602081015161a4a0525060206157835f395f5163f3fef3a361a500523361a52052670de0b6b3a764000061a54052604061a500604461a51c5f855af1611ed4573d5f5f3e3d5ffd5b60403d1061567b5761a5009050805161a4c052602081015161a4e0525060403661a5003733604052611f0761a540613604565b61a540805161a50052602081015161a520525060206157036040396020615783606039617c405160805261a4e05160a052611f40613eec565b7e8ae1880000000000000000000000000000000000000000000000000000000061a540525f61a5805261a580805160208201209050617d2051617d402018611fa8577fef67dc740000000000000000000000000000000000000000000000000000000061a540525b617c405160405261a540516060523360805261a4c05160a05261a4e05160c05261a5005160e052617c605160208160051b018061010082617c6060045afa5050506020617d205101806101c082617d2060045afa505061200961a5c0613fdc565b61a5c0805161a56052602081015161a58052604081015161a5a0525061a5805161a4c05180820182811061567b579050905061a5c05261a5c0511561567b575f61a5e05261a5005161a5c051101561220a57600161a4805161a4a05103015f811261567b5761a6005261a5605161a48051131561567b5761a5805161a5e05261a5805161a500510361a5005261a5a0516101605261a500516101805261a600516101a0526120b861a6406137f3565b61a6405161a6205261a6205161a4805161a4a051038082018281125f83121861567b579050905061a6405260206157835f395f5163ab047e0061a660523361a6805261a5a05161a6a05261a6205161a6c05261a6405161a6e052803b1561567b575f61a660608461a67c5f855af1612132573d5f5f3e3d5ffd5b50680100000000000000095461a6605261a660516002336020525f5260405f20556020615703604039617c4051606052602061578360803961a5a05160a052612179613eec565b6020615743604039617c40516060523060805261a5805160a05261219b613eec565b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f261a5a05161a6805261a5005161a6a05261a6205161a6c05261a6405161a6e05261a6605161a7005260a061a680a261a4e05161a5a05180820382811161567b579050905061a4e0526122e8565b61a5005161a5e0525f61a5005233604052612223614bb8565b6020615743604039617c40516060523060805261a5805160a052612245613eec565b602061574360403960206157836060393060805261a4c05160a052612268613eec565b61a5e05161a5c05111156122965760206157436040393360605261a5e05161a5c05103608052612296613f68565b6020615703604039617c40516060523360805261a5a05160a0526122b8613eec565b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a6003760a061a600a25b337f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a4e05161a6005261a5e05161a62052604061a600a2680100000000000000075461a5e05180820182811061567b579050905068010000000000000007556001336020525f5260405f2061a50051815561a5205160018201555060035461a5205180820281158383830414171561567b5790509050600454801561567b578082049050905061a6005261a5e05161a6005161a5e051808281188284110218905090500360035561a520516004556123bf613577565b60035f55005b632a0c358681186132205760443610341761567b575f5460021461567b5760025f5560206156e35f395f5163f851a440604052602060406004605c845afa61240f573d5f5f3e3d5ffd5b60203d1061567b576040518060a01c61567b576080526080905051331861567b57602435600435111561567b57662386f26fc100006024351061567b576706f05b59d3b200006004351161567b5760243568010000000000000009556004356801000000000000000a557fe2750bf9a7458977fcc01c1a0b615d12162f63b18cad78441bd64c590b337eca6040600460403760406040a160035f5500613220565b630b8db68181186124cf5760843610341761567b575f610340526107b1565b631b1800e38118613220573461567b5760206157835f395f5163095a0fc6606052602060606004607c845afa612507573d5f5f3e3d5ffd5b60203d1061567b57606090505160405260035460605260045460805260605160405180820281158383830414171561567b5790509050608051801561567b5780820490509050680100000000000000075480820182811061567b5790509050606052680100000000000000065460a05260a05160605160a051808281188284110218905090500360c052602060c0f3613220565b63bcbaf487811861262e5760443610341761567b576004358060a01c61567b5761a6a0525f5460021461567b5760025f555f61a6c0523361a6a051146125f057600261a6a0516020525f5260405f205461a6c0525b61a6a051617c4052602435617c605261a6c051617c8052670de0b6b3a7640000617ca052604036617cc0375f617da052612628614ffc565b60035f55005b6381d2f1b781186132205760243610341761567b576004358060a01c61567b576040525f5460021461567b5760025f5560206156e35f395f5163f851a440606052602060606004607c845afa612686573d5f5f3e3d5ffd5b60203d1061567b576060518060a01c61567b5760a05260a0905051331861567b57604051680100000000000000085560405163e91f2f4c606052602060606004607c5f855af16126d8573d5f5f3e3d5ffd5b60203d1061567b57606050507f51fabb88f7860c9dbcc2a5a9b69a8b9476d63b87124591f97254e29f0e8daaeb60405160605260206060a160035f5500613220565b63fed9698c81186132205760c43610341761567b575f61a7a05261277156613220565b63d28952fc8118613220576101043610341761567b5760a43560040161271081351161567b576020813501808261a7a03750505b6004358060a01c61567b5761a6a0526064358060a01c61567b5761a6c052608435600401600581351161567b57803560208160051b01808361a6e0375050505f5460021461567b5760025f555f61cf00523361a6a051146127e157600261a6a0516020525f5260405f205461cf00525b61a6a051617c4052602435617c605261cf0051617c8052604435670de0b6b3a7640000818118670de0b6b3a7640000831002189050617ca05261a6c051617cc05261a6e05160208160051b0180617ce08261a6e060045afa505050602061a7a0510180617da08261a7a060045afa5050612859614ffc565b60035f5500613220565b631b25cdaf811861288a5760243610341761567b57670de0b6b3a764000061010052612b39565b6390f8667d81186132205760443610341761567b57604060046101e0375b5f5460021461567b576801000000000000000554610220526102005161024052610200516128d95761022051610240525b6101e051610260525f610280525f620f4240905b80620273a052610220516102605110156129115761024051620273a0511815612914565b60015b1561291e57612a89565b6102605167fffffffffffffffe811161567b5760050154620273c052620273c05160405261294e62027400613604565b6202740051620273e052620273c051604052620273e05160605260016080526002620273c0516020525f5260405f205460a05261298d62027420614c8c565b620274205162027400527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff620274005113612a695760206157835f395f5163544fb5c16202746052620273c051620274805260406202746060246202747c845afa6129fa573d5f5f3e3d5ffd5b60403d1061567b57620274609050805162027420526020810151620274405250610280516103e7811161567b5760a081026102a001620273c05181526202742051602082015262027440516040820152620273e051606082015262027400516080820152506001810161028052505b610260516001810181811061567b579050610260526001018181186128ed575b5050602080620273a05280620273a0015f6102805180835260a081025f826103e8811161567b578015612b0257905b60a08102602087010160a082026102a00180518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050600101818118612ab8575b50508201602001915050905081019050620273a0f3613220565b63546e040d81186132205760443610341761567b57602435610100525b6004358060a01c61567b5760e0525f5460021461567b575f610120523360e05114612b7257600260e0516020525f5260405f2054610120525b670de0b6b3a764000060206157835f395f5163544fb5c16101605260e051610180526040610160602461017c845afa612bad573d5f5f3e3d5ffd5b60403d1061567b576101609050516101005160405261012051606052612bd46101c0614f8f565b6101c05180820281158383830414171561567b57905090500461014052670de0b6b3a764000060e051604052612c0b610180613604565b610180516101005180820281158383830414171561567b579050905004610160526101405161016051610140518082811882841102189050905003610180526020610180f3613220565b638908ea8281186132205760443610341761567b576024358060011c61567b57610200525b6004358060a01c61567b576101e0525f5460021461567b5760206101e051610280526101e051604052612cae610220613604565b610220516102a052610200516102c05260026101e0516020525f5260405f20546102e052610280516040526102a0516060526102c0516080526102e05160a052612cf9610260614c8c565b610260f3613220565b627c98ab8118612d1d573461567b576040366101e0376128a8565b63cc1891c781186132205760243610341761567b576004358060a01c61567b576040525f5460021461567b5760025f5560206156e35f395f5163f851a440606052602060606004607c845afa612d75573d5f5f3e3d5ffd5b60203d1061567b576060518060a01c61567b5760a05260a0905051331861567b5760206157835f395f5163cc1891c7606052604051608052803b1561567b575f60606024607c5f855af1612dcb573d5f5f3e3d5ffd5b5060035f5500613220565b632c5089c381186132205760243610341761567b576004358060a01c61567b576040525f5460021461567b5760206157835f395f5163e8dd1ef1606052604051608052602060606024607c845afa612e30573d5f5f3e3d5ffd5b60203d1061567b576060518060011c61567b5760a05260a09050511561567b5760206157835f395f5163b461100d60a05260405160c052604060a0602460bc845afa612e7e573d5f5f3e3d5ffd5b60403d1061567b5760a09050805160605260208101516080525060206157835f395f51632eb858e760a05260605160c052602060a0602460bc845afa612ec6573d5f5f3e3d5ffd5b60203d1061567b5760a09050516101205260206157835f395f516324299b7a60e05260805161010052602060e0602460fc845afa612f06573d5f5f3e3d5ffd5b60203d1061567b5760e0905051610140526040610120f3613220565b631e0cfcef8118613220573461567b575f5460021461567b5760025f5560206156e35f395f5163cab4d3db610180526020610180600461019c845afa612f6a573d5f5f3e3d5ffd5b60203d1061567b57610180518060a01c61567b576101c0526101c09050516101605260206157835f395f5163d1fea7336101a05260206101a060046101bc845afa612fb7573d5f5f3e3d5ffd5b60203d1061567b576101a09050516101805260206157835f395f516389960ba76101c05260206101c060046101dc845afa612ff4573d5f5f3e3d5ffd5b60203d1061567b576101c09050516101a05260206157436040396020615783606039610160516080526101805160a05261302c613eec565b60206157036040396020615783606039610160516080526101a05160a052613052613eec565b60206157835f395f5163822fe5076101c052803b1561567b575f6101c060046101dc5f855af1613084573d5f5f3e3d5ffd5b5060206157835f395f5163095a0fc66101e05260206101e060046101fc845afa6130b0573d5f5f3e3d5ffd5b60203d1061567b576101e09050516101c0526003546101e052600454610200526101e0516101c05180820281158383830414171561567b579050905061020051801561567b57808204905090506101e0526101c051610200526101e0516003556102005160045561311f613577565b6101e051680100000000000000075480820182811061567b5790509050610220526801000000000000000654610240526102405161022051116131a3577f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f5f610260526101e051610280526040610260a15f61026052602061026061321a5661321a565b61022051680100000000000000065561024051610220510361022052602061574360403961016051606052610220516080526131dd613f68565b7f5393ab6ef9bb40d91d1b04bbbeb707fbf3d1eb73f46744e2d179e4996026283f61022051610260526101e051610280526040610260a160206102205b60035f55f35b5f5ffd5b6040516060525f60805260405160801c156132485760405160801c60605260806080525b60605160401c156132665760605160401c6060526040608051016080525b60605160201c156132845760605160201c6060526020608051016080525b60605160101c156132a25760605160101c6060526010608051016080525b60605160081c156132c05760605160081c6060526008608051016080525b60605160041c156132de5760605160041c6060526004608051016080525b60605160021c156132fc5760605160021c6060526002608051016080525b60605160011c15613311576001608051016080525b608051815250565b60a0518060ff1c61567b5760c05260a0511561567b57606060a051604052613342610100613224565b610100518060ff1c61567b570360e05260c05160e051609f035f811261567b571b609f1c8060ff1c61567b5760c0526d0139601a2efabe717e604cbb489460c0516c29508e458543d8aa4df2abee7860c051010260601d01610100526d02247f7a7b6594320649aa03aba160c051610100510260601d01610100526c8c3f38e95a6b1ff2ab1c3b343760c051610100510260601d03610100526d02384773bdf1ac5676facced609160c051610100510260601d03610100526cb9a025d814b29c212b8b1a07ce60c051610100510260601d0361010052780a09507084cc699bb0e71ea86a00000000000000000000000060c051610100510203610100526d0388eaa27412d5aca026815d636e60c0516c465772b2bbbb5f824b15207a3060c051010260601d01610120526d0df99ac502031bf953eff472fdcc60c051610120510260601d01610120526d13cdffb29d51d99322bdff5f221160c051610120510260601d01610120526d0a0f742023def783a307a986912e60c051610120510260601d01610120526d01920d8043ca89b5239253284e4260c051610120510260601d01610120526c0b7a86d7375468fac667a0a52760c051610120510260601d0161012052610120516101005105610140527d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642847d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b360e05102711340daa0d5f769dba1915cef59f0815a55066101405102010160ae1d815250565b680100000000000000085463e91f2f4c606052602060606004607c5f855af16135a2573d5f5f3e3d5ffd5b60203d1061567b576060905051640a3c2abcef818118640a3c2abcef83100218905060405260206157835f395f5163d4387a99606052604051608052602060606024607c5f855af16135f6573d5f5f3e3d5ffd5b60203d1061567b5760605050565b60206157835f395f5163095a0fc6608052602060806004609c845afa61362c573d5f5f3e3d5ffd5b60203d1061567b57608090505160605260016040516020525f5260405f208054608052600181015460a05250608051613674575f81526060516020820152506136f8566136f8565b60805160605180820281158383830414171561567b579050905060c05260c05160a051801561567b5780820690509050156136d35760026801000000000000000554106136d35760c05160a05180820182811061567b579050905060c0525b60c05160a051801561567b578082049050905060c05260c05181526060516020820152505b565b604051608051606051604051046103e88181186103e8831102189050683635c9adc5dea000000480820182811061567b5790509050670de0b6b3a7640000818118670de0b6b3a7640000831002189050670de0b6b3a76400000380820281158383830414171561567b579050905060605160206158035f395f5102801561567b578082049050905060a05260a05160c052600160318101905b8060e05260605160e051186137a7576137e9565b60206157a35f395f5160a05160206157c35f395f5180820281158383830414171561567b57905090500460a05260a05160c0510160c052600101818118613793575b505060c051815250565b610180516138605760076101c0527f4e6f206c6f616e000000000000000000000000000000000000000000000000006101e0526101c0506101c051806101e001601f825f031636823750506308c379a06101805260206101a052601f19601f6101c051011660440161019cfd5b60206157835f395f51638f8654c56101e05260206101e060046101fc845afa61388b573d5f5f3e3d5ffd5b60203d1061567b576101e09050516101c05260206157835f395f51632eb858e7610200526101c051610220526020610200602461021c845afa6138d0573d5f5f3e3d5ffd5b60203d1061567b576102009050516101e0526101605160206157235f395f5180820281158383830414171561567b57905090506040526101a0516060526801000000000000000a546080526139266102206136fa565b61022051610200526101805160206157635f395f5180820281158383830414171561567b57905090506001810181811061567b579050610200516101e05180820281158383830414171561567b57905090500461020052610200516139ea57600e610220527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006102405261022050610220518061024001601f825f031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b6102005160a0526139fc610240613319565b61024051610220527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102205113613a555761022051600160206157e35f395f51038082038281135f83121861567b5790509050610220525b60206157e35f395f51610220510561022052610220516101a0518060ff1c61567b57806104000361040081135f83121861567b579050808281188284120218905090506101c0518082018281125f83121861567b5790509050610220526101c0516102205113613b865760206157835f395f5163ec65470661024052610220516001810381811361567b579050610260526020610240602461025c845afa613aff573d5f5f3e3d5ffd5b60203d1061567b57610240518060011c61567b5761028052610280905051613b8657600d6102a0527f4465627420746f6f2068696768000000000000000000000000000000000000006102c0526102a0506102a051806102c001601f825f031636823750506308c379a061026052602061028052601f19601f6102a051011660440161027cfd5b60206157835f395f516386fc88d3610280526020610280600461029c845afa613bb1573d5f5f3e3d5ffd5b60203d1061567b5761028090505160206157835f395f51632eb858e76102405261022051610260526020610240602461025c845afa613bf2573d5f5f3e3d5ffd5b60203d1061567b5761024090505110613c6a57600d6102c0527f4465627420746f6f2068696768000000000000000000000000000000000000006102e0526102c0506102c051806102e001601f825f031636823750506308c379a06102805260206102a052601f19601f6102c051011660440161029cfd5b61022051815250565b60206157835f395f516386fc88d3610180526020610180600461019c845afa613c9e573d5f5f3e3d5ffd5b60203d1061567b576101809050516101605260206157835f395f5163a7db79a56101a05260206101a060046101bc845afa613cdb573d5f5f3e3d5ffd5b60203d1061567b576101a0905051670de0b6b3a7640000810281670de0b6b3a764000082041861567b57905061016051801561567b578082049050905060a052613d266101e0613319565b6101e051610180527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101805113613d89576101805160206157e35f395f516001810381811361567b5790508082038281135f83121861567b5790509050610180525b60206157e35f395f5161018051056005810181811261567b5790506101805260206157835f395f5163c16ef2646101c05260206101c060046101dc845afa613dd3573d5f5f3e3d5ffd5b60203d1061567b576101c09050516101a052610180516101a0516001810181811261567b579050808281188284130218905090506101805260206157835f395f51632eb858e76101e052610180516102005260206101e060246101fc845afa613e3e573d5f5f3e3d5ffd5b60203d1061567b576101e09050516101c0525f610401905b806101e052610180516001810381811361567b579050610180526101a0516101805113613e8257613ee0565b6101c0516102005260206157c35f395f516101c05160206157a35f395f5180820281158383830414171561567b5790509050046101c052610160516101c0511115613ed557610200518352505050613eea565b600101818118613e56575b50506101c0518152505b565b60a05115613f66576040516323b872dd60c05260605160e0526080516101005260a05161012052602060c0606460dc5f855af1613f2b573d5f5f3e3d5ffd5b3d613f4257803b1561567b57600161014052613f5a565b60203d1061567b5760c0518060011c61567b57610140525b6101409050511561567b575b565b60805115613fda5760405163a9059cbb60a05260605160c05260805160e052602060a0604460bc5f855af1613f9f573d5f5f3e3d5ffd5b3d613fb657803b1561567b57600161010052613fce565b60203d1061567b5760a0518060011c61567b57610100525b6101009050511561567b575b565b60206157035f395f516040511461567b576060366129003760206157835f395f51638f8654c5612960526020612960600461297c845afa61401f573d5f5f3e3d5ffd5b60203d1061567b576129609050516129005260206157835f395f5163ebcb006761298052612900516129a0526020612980602461299c845afa614064573d5f5f3e3d5ffd5b60203d1061567b576129809050516129605260206157835f395f516331f7e3066129a052612900516129c05260206129a060246129bc845afa6140a9573d5f5f3e3d5ffd5b60203d1061567b576129a0905051612980526040515a5f60605181615300015260048101905060c0608051612a205260a051612a405260c051612a605260e051612a805280612aa05280612a20015f610100518083528060051b5f826005811161567b57801561413357905b8060051b61012001518160051b602088010152600101818118614115575b5050820160200191505090508101905080612ac05280612a200160206101c051018082826101c060045afa50508051806020830101601f825f03163682375050601f19601f82516020010116905081019050612a0052612a0080516020820183615300018281848460045afa50505080830192505050806152e0526152e050506040617c006152e0516153005f8686f1905090506141d3573d5f5f3e3d5ffd5b3d604081183d6040100218617be052617be06020815101806129a0828460045afa5050506129a05160201161567b576129c051612a20526020612a0052612a006020810151815160200360031b1c9050612920526129a05160401161567b576129e051612a20526020612a0052612a006020810151815160200360031b1c90506129405260206157835f395f51638f8654c5612a00526020612a006004612a1c845afa614282573d5f5f3e3d5ffd5b60203d1061567b57612a00905051612900511861567b5760206157835f395f5163ebcb0067612a005261290051612a20526020612a006024612a1c845afa6142cc573d5f5f3e3d5ffd5b60203d1061567b57612a00905051612960511861567b5760206157835f395f516331f7e306612a005261290051612a20526020612a006024612a1c845afa614316573d5f5f3e3d5ffd5b60203d1061567b57612a00905051612980511861567b5761290051815261292051602082015261294051604082015250565b6001336020525f5260405f2054156143bf576014610380527f4c6f616e20616c726561647920637265617465640000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b600461034051101561443057600f610380527f4e656564206d6f7265207469636b7300000000000000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b60326103405111156144a157600f610380527f4e656564206c657373207469636b7300000000000000000000000000000000006103a0526103805061038051806103a001601f825f031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b61030051610160526103205161018052610340516101a0526144c46103a06137f3565b6103a0516103805261038051610340516001810381811161567b5790508060ff1c61567b578082018281125f83121861567b57905090506103a05260206157835f395f5163095a0fc66103e05260206103e060046103fc845afa61452a573d5f5f3e3d5ffd5b60203d1061567b576103e09050516103c0526001336020525f5260405f206103205181556103c05160018201555068010000000000000009546103e0526103e0516002336020525f5260405f2055680100000000000000055461040052336104005167fffffffffffffffe811161567b57600501556104005168010000000000000004336020525f5260405f20556001610400510168010000000000000005556003546103c05180820281158383830414171561567b5790509050600454801561567b57808204905090506103205180820182811061567b57905090506003556103c05160045560206157835f395f5163ab047e00610420523361044052610300516104605261038051610480526103a0516104a052803b1561567b575f610420608461043c5f855af1614660573d5f5f3e3d5ffd5b5068010000000000000006546103205180820182811061567b5790509050680100000000000000065561036051156146d05760206157036040393360605260206157836080396103005160a0526146b5613eec565b602061574360403933606052610320516080526146d0613f68565b6146d8613577565b337feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f26103005161042052610320516104405261038051610460526103a051610480526103e0516104a05260a0610420a2337fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610300516104205261032051610440526040610420a2565b604036610380376103405160405261477b6103c0613604565b6103c080516103805260208101516103a05250610380516147fb5760126103c0527f4c6f616e20646f65736e277420657869737400000000000000000000000000006103e0526103c0506103c051806103e001601f825f031636823750506308c379a06103805260206103a052601f19601f6103c051011660440161039cfd5b610380516103205180820182811061567b57905090506103805260206157835f395f5163b461100d6104005261034051610420526040610400602461041c845afa614848573d5f5f3e3d5ffd5b60403d1061567b57610400905080516103c05260208101516103e0525060016103c0516103e05103015f811261567b576104005260206157835f395f5163f3fef3a3610460526103405161048052670de0b6b3a76400006104a0526040610460604461047c5f855af16148bd573d5f5f3e3d5ffd5b60403d1061567b57610460905080516104205260208101516104405250610420511561494857601a610460527f416c726561647920696e20756e6465727761746572206d6f64650000000000006104805261046050610460518061048001601f825f031636823750506308c379a061042052602061044052601f19601f61046051011660440161043cfd5b6103605161496f57610440516103005180820182811061567b57905090506104405261498a565b610440516103005180820382811161567b5790509050610440525b61044051610160526103805161018052610400516101a0526149ad6104806137f3565b6104805161046052610460516103c0516103e051038082018281125f83121861567b57905090506104805260206157835f395f5163ab047e006104a052610340516104c052610440516104e05261046051610500526104805161052052803b1561567b575f6104a060846104bc5f855af1614a2a573d5f5f3e3d5ffd5b506001610340516020525f5260405f206103805181556103a0516001820155505f6104a052336103405118614a815768010000000000000009546104a0526104a0516002610340516020525f5260405f2055614a96565b6002610340516020525f5260405f20546104a0525b6103205115614ae7576003546103a05180820281158383830414171561567b5790509050600454801561567b57808204905090506103205180820182811061567b57905090506003556103a0516004555b61036051614b2f57610340517fe1979fe4c35e0cef342fef5668e2c8e7a7e9f5d5d1ca8fee0ac6c427fa4153af610300516104c052610320516104e05260406104c0a2614b63565b610340517fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb54610300516104c05260206104c0a25b610340517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f2610440516104c052610380516104e052610460516105005261048051610520526104a0516105405260a06104c0a2565b68010000000000000005546001810381811161567b579050606052680100000000000000046040516020525f5260405f205460805260405160805167fffffffffffffffe811161567b57600501541861567b575f680100000000000000046040516020525f5260405f20556060516080511015614c7c5760605167fffffffffffffffe811161567b576005015460a05260a05160805167fffffffffffffffe811161567b57600501556080516801000000000000000460a0516020525f5260405f20555b6060516801000000000000000555565b606051614cef57601260c0527f4c6f616e20646f65736e2774206578697374000000000000000000000000000060e05260c05060c0518060e001601f825f031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60a0518060ff1c61567b5780670de0b6b3a764000003670de0b6b3a764000081135f83121861567b57905060c0526060518060ff1c61567b5760206157835f395f516362ca4b1860e05260405161010052602060e0602460fc845afa614d57573d5f5f3e3d5ffd5b60203d1061567b5760e09050518060ff1c61567b5760c05180820281191515600160ff1b841415178215848484051417161561567b579050905005670de0b6b3a7640000810381811361567b57905060c05260805115614f875760206157835f395f5163b461100d61010052604051610120526040610100602461011c845afa614de3573d5f5f3e3d5ffd5b60403d1061567b5761010090505160e05260206157835f395f51638f8654c5610100526020610100600461011c845afa614e1f573d5f5f3e3d5ffd5b60203d1061567b5761010090505160e0511315614f875760206157835f395f516386fc88d3610160526020610160600461017c845afa614e61573d5f5f3e3d5ffd5b60203d1061567b576101609050516101405260206157835f395f51632eb858e76101805260e0516101a0526020610180602461019c845afa614ea5573d5f5f3e3d5ffd5b60203d1061567b576101809050516101605261016051610140511115614f875760c05160605160206157635f395f5180820281158383830414171561567b579050905061016051610140510360206157835f395f5163544fb5c1610180526040516101a0526040610180602461019c845afa614f23573d5f5f3e3d5ffd5b60403d1061567b5761018090506020810190505180820281158383830414171561567b579050905060206157235f395f5180820281158383830414171561567b5790509050048060ff1c61567b578082018281125f83121861567b579050905060c0525b60c051815250565b670de0b6b3a7640000608052670de0b6b3a763ffff60405111614ff457606051670de0b6b3a764000001604051670de0b6b3a76400000360605160011c670de0b6b3a7640000010204608052670de0b6b3a76400006040516040516080510102046080525b608051815250565b60403661a4e037617c405160405261501561a520613604565b61a520805161a4e052602081015161a5005250617c8051156150e6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff617c405160405261a4e0516060526001608052617c805160a05261507761a520614c8c565b61a5205113156150e657600f61a540527f4e6f7420656e6f7567682072656b74000000000000000000000000000000000061a5605261a5405061a540518061a56001601f825f031636823750506308c379a061a50052602061a52052601f19601f61a54051011660440161a51cfd5b61a4e05161a52052670de0b6b3a764000061a4e051617ca05180820281158383830414171561567b57905090500461a4e05261a4e0511561567b5761a4e05161a520510361a5205260206157835f395f5163f3fef3a361a5a052617c405161a5c052617ca051604052617c805160605261516161a580614f8f565b61a5805161a5e052604061a5a0604461a5bc5f855af1615183573d5f5f3e3d5ffd5b60403d1061567b5761a5a09050805161a54052602081015161a5605250617c605161a54051101561521357600861a580527f536c69707061676500000000000000000000000000000000000000000000000061a5a05261a5805061a580518061a5a001601f825f031636823750506308c379a061a54052602061a56052601f19601f61a58051011660440161a55cfd5b61a5405161a4e0518082811882841002189050905061a58052602061574360403960206157836060393060805261a5805160a05261524f613eec565b61a5405161a4e051116152b957602061570360403960206157836060393360805261a5605160a05261527f613eec565b61a4e05161a54051111561550f57602061574360403960206157836060393360805261a4e05161a540510360a05261550f613eec5661550f565b61a5405161a4e0510361a5a052617cc05161531457602061570360403960206157836060393360805261a5605160a0526152f1613eec565b6020615743604039336060523060805261a5a05160a05261550f613eec5661550f565b60206157036040396020615783606039617cc05160805261a5605160a05261533a613eec565b7f750abc510000000000000000000000000000000000000000000000000000000061a5c0525f61a6005261a600805160208201209050617da051617dc020186153a3577f4ea696bb0000000000000000000000000000000000000000000000000000000061a5c0525b617cc05160405261a5c051606052617c405160805261a5405160a05261a5605160c05261a4e05160e052617ce05160208160051b018061010082617ce060045afa5050506020617da05101806101c082617da060045afa505061540761a640613fdc565b61a640805161a5e052602081015161a60052604081015161a620525061a5a05161a60051101561549657601361a640527f6e6f7420656e6f7567682070726f63656564730000000000000000000000000061a6605261a6405061a640518061a66001601f825f031636823750506308c379a061a60052602061a62052601f19601f61a64051011660440161a61cfd5b61a5a05161a6005111156154cb576020615743604039617cc0516060523360805261a5a05161a600510360a0526154cb613eec565b6020615743604039617cc0516060523060805261a5a05160a0526154ed613eec565b6020615703604039617cc0516060523360805261a6205160a05261550f613eec565b680100000000000000075461a4e05180820182811061567b579050905068010000000000000007556001617c40516020525f5260405f2061a52051815561a50051600182015550617c40517f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d061a5605161a5a05261a4e05161a5c052604061a5a0a2617c4051337f642dd4d37ddd32036b9797cec464c0045dd2118c549066ae6b0f88e32240c2d061a5605161a5a05261a5405161a5c05261a4e05161a5e052606061a5a0a361a5205161561e57617c40517feec6b7095a637e006c79c1819d696e353a8f703db2c49fc0219e17a8fd04f7f260a03661a5a03760a061a5a0a2617c405160405261561e614bb8565b60035461a5005180820281158383830414171561567b5790509050600454801561567b578082049050905061a5a05261a4e05161a5a05161a4e051808281188284110218905090500360035561a50051600455615679613577565b565b5f80fd0e552dd63220259b0d771abb1dd904ac17931d87016e0560196f2f222c5532200424053e1b4e28630dee32201d3f24b03220322003d43220322000183220322015293220322004f2273d17320d5515b402f12b1c0484322003fc3220271a2d0232201d1c000000000000000000000000ff467c6e827ebbea64da1ab0425021e6c89fbe0d000000000000000000000000d9a442856c234a39a81a089c06451ebaa4306a720000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd28cf8f7755f03967d27e128b38022b6391983600000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000006d52a917fb3e980000000000000000000000000000000000000000000000000e17cc3c5a3ea485000000000000000000000000000000000000000000000000016345785d8a0000
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e478df674e25b2724b1ac9bcaba644943da359f000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000f8b0a10e470008000000000000000000000000cd28cf8f7755f03967d27e128b38022b63919836
-----Decoded View---------------
Arg [0] : collateral_token (address): 0x0000000000000000000000000000000000000000
Arg [1] : monetary_policy (address): 0x2e478Df674e25b2724B1aC9BCABa644943dA359F
Arg [2] : loan_discount (uint256): 100000000000000000
Arg [3] : liquidation_discount (uint256): 70000000000000008
Arg [4] : amm (address): 0xcd28cF8f7755f03967D27E128B38022B63919836
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 0000000000000000000000002e478df674e25b2724b1ac9bcaba644943da359f
Arg [2] : 000000000000000000000000000000000000000000000000016345785d8a0000
Arg [3] : 00000000000000000000000000000000000000000000000000f8b0a10e470008
Arg [4] : 000000000000000000000000cd28cf8f7755f03967d27e128b38022b63919836
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $0.99948 | 1,015,964.4826 | $1,015,436.18 |
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.