ERC-20
Overview
Max Total Supply
5,732.489423992063145843 ZUNWETH
Holders
3
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Balance
0 ZUNWETHValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Source Code Verified (Exact Match)
Contract Name:
CurveTwocryptoOptimized
Compiler Version
vyper:0.3.10
Contract Source Code (Vyper language format)
# pragma version 0.3.10 # pragma optimize gas # pragma evm-version paris """ @title CurveTwocryptoOptimized @author Curve.Fi @license Copyright (c) Curve.Fi, 2023 - all rights reserved @notice A Curve AMM pool for 2 unpegged assets (e.g. WETH, USD). @dev All prices in the AMM are with respect to the first token in the pool. """ from vyper.interfaces import ERC20 implements: ERC20 # <--------------------- AMM contract is also the LP token. # --------------------------------- Interfaces ------------------------------- interface Math: def wad_exp(_power: int256) -> uint256: view def newton_D( ANN: uint256, gamma: uint256, x_unsorted: uint256[N_COINS], K0_prev: uint256 ) -> uint256: view def get_y( ANN: uint256, gamma: uint256, x: uint256[N_COINS], D: uint256, i: uint256, ) -> uint256[2]: view def get_p( _xp: uint256[N_COINS], _D: uint256, _A_gamma: uint256[2], ) -> uint256: view interface Factory: def admin() -> address: view def fee_receiver() -> address: view def views_implementation() -> address: view interface Views: def calc_token_amount( amounts: uint256[N_COINS], deposit: bool, swap: address ) -> uint256: view def get_dy( i: uint256, j: uint256, dx: uint256, swap: address ) -> uint256: view def get_dx( i: uint256, j: uint256, dy: uint256, swap: address ) -> uint256: view # ------------------------------- Events ------------------------------------- event Transfer: sender: indexed(address) receiver: indexed(address) value: uint256 event Approval: owner: indexed(address) spender: indexed(address) value: uint256 event TokenExchange: buyer: indexed(address) sold_id: uint256 tokens_sold: uint256 bought_id: uint256 tokens_bought: uint256 fee: uint256 packed_price_scale: uint256 event AddLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] fee: uint256 token_supply: uint256 packed_price_scale: uint256 event RemoveLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] token_supply: uint256 event RemoveLiquidityOne: provider: indexed(address) token_amount: uint256 coin_index: uint256 coin_amount: uint256 approx_fee: uint256 packed_price_scale: uint256 event NewParameters: mid_fee: uint256 out_fee: uint256 fee_gamma: uint256 allowed_extra_profit: uint256 adjustment_step: uint256 ma_time: uint256 xcp_ma_time: uint256 event RampAgamma: initial_A: uint256 future_A: uint256 initial_gamma: uint256 future_gamma: uint256 initial_time: uint256 future_time: uint256 event StopRampA: current_A: uint256 current_gamma: uint256 time: uint256 event ClaimAdminFee: admin: indexed(address) tokens: uint256[N_COINS] # ----------------------- Storage/State Variables ---------------------------- N_COINS: constant(uint256) = 2 PRECISION: constant(uint256) = 10**18 # <------- The precision to convert to. PRECISIONS: immutable(uint256[N_COINS]) MATH: public(immutable(Math)) coins: public(immutable(address[N_COINS])) factory: public(immutable(Factory)) cached_price_scale: uint256 # <------------------------ Internal price scale. cached_price_oracle: uint256 # <------- Price target given by moving average. cached_xcp_oracle: uint256 # <----------- EMA of totalSupply * virtual_price. last_prices: public(uint256) last_timestamp: public(uint256) # idx 0 is for prices, idx 1 is for xcp. last_xcp: public(uint256) xcp_ma_time: public(uint256) initial_A_gamma: public(uint256) initial_A_gamma_time: public(uint256) future_A_gamma: public(uint256) future_A_gamma_time: public(uint256) # <------ Time when ramping is finished. # This value is 0 (default) when pool is first deployed, and only gets # populated by block.timestamp + future_time in `ramp_A_gamma` when the # ramping process is initiated. After ramping is finished # (i.e. self.future_A_gamma_time < block.timestamp), the variable is left # and not set to 0. balances: public(uint256[N_COINS]) D: public(uint256) xcp_profit: public(uint256) xcp_profit_a: public(uint256) # <--- Full profit at last claim of admin fees. virtual_price: public(uint256) # <------ Cached (fast to read) virtual price. # The cached `virtual_price` is also used internally. # Params that affect how price_scale get adjusted : packed_rebalancing_params: public(uint256) # <---------- Contains rebalancing # parameters allowed_extra_profit, adjustment_step, and ma_time. # Fee params that determine dynamic fees: packed_fee_params: public(uint256) # <---- Packs mid_fee, out_fee, fee_gamma. ADMIN_FEE: public(constant(uint256)) = 5 * 10**9 # <----- 50% of earned fees. MIN_FEE: constant(uint256) = 5 * 10**5 # <-------------------------- 0.5 BPS. MAX_FEE: constant(uint256) = 10 * 10**9 NOISE_FEE: constant(uint256) = 10**5 # <---------------------------- 0.1 BPS. # ----------------------- Admin params --------------------------------------- last_admin_fee_claim_timestamp: uint256 admin_lp_virtual_balance: uint256 MIN_RAMP_TIME: constant(uint256) = 86400 MIN_ADMIN_FEE_CLAIM_INTERVAL: constant(uint256) = 86400 A_MULTIPLIER: constant(uint256) = 10000 MIN_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER / 10 MAX_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER * 1000 MAX_A_CHANGE: constant(uint256) = 10 MIN_GAMMA: constant(uint256) = 10**10 MAX_GAMMA: constant(uint256) = 5 * 10**16 # ----------------------- ERC20 Specific vars -------------------------------- name: public(immutable(String[64])) symbol: public(immutable(String[32])) decimals: public(constant(uint8)) = 18 version: public(constant(String[8])) = "v2.0.0" balanceOf: public(HashMap[address, uint256]) allowance: public(HashMap[address, HashMap[address, uint256]]) totalSupply: public(uint256) nonces: public(HashMap[address, uint256]) EIP712_TYPEHASH: constant(bytes32) = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" ) EIP2612_TYPEHASH: constant(bytes32) = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ) VERSION_HASH: constant(bytes32) = keccak256(version) NAME_HASH: immutable(bytes32) CACHED_CHAIN_ID: immutable(uint256) salt: public(immutable(bytes32)) CACHED_DOMAIN_SEPARATOR: immutable(bytes32) # ----------------------- Contract ------------------------------------------- @external def __init__( _name: String[64], _symbol: String[32], _coins: address[N_COINS], _math: address, _salt: bytes32, packed_precisions: uint256, packed_gamma_A: uint256, packed_fee_params: uint256, packed_rebalancing_params: uint256, initial_price: uint256, ): MATH = Math(_math) factory = Factory(msg.sender) name = _name symbol = _symbol coins = _coins PRECISIONS = self._unpack_2(packed_precisions) # <-- Precisions of coins. # --------------- Validate A and gamma parameters here and not in factory. gamma_A: uint256[2] = self._unpack_2(packed_gamma_A) # gamma is at idx 0. assert gamma_A[0] > MIN_GAMMA-1 assert gamma_A[0] < MAX_GAMMA+1 assert gamma_A[1] > MIN_A-1 assert gamma_A[1] < MAX_A+1 self.initial_A_gamma = packed_gamma_A self.future_A_gamma = packed_gamma_A # ------------------------------------------------------------------------ self.packed_rebalancing_params = packed_rebalancing_params # <-- Contains # rebalancing params: allowed_extra_profit, adjustment_step, # and ma_exp_time. self.packed_fee_params = packed_fee_params # <-------------- Contains Fee # params: mid_fee, out_fee and fee_gamma. self.cached_price_scale = initial_price self.cached_price_oracle = initial_price self.last_prices = initial_price self.last_timestamp = self._pack_2(block.timestamp, block.timestamp) self.xcp_profit_a = 10**18 self.xcp_ma_time = 62324 # <--------- 12 hours default on contract start. # Cache DOMAIN_SEPARATOR. If chain.id is not CACHED_CHAIN_ID, then # DOMAIN_SEPARATOR will be re-calculated each time `permit` is called. # Otherwise, it will always use CACHED_DOMAIN_SEPARATOR. # see: `_domain_separator()` for its implementation. NAME_HASH = keccak256(name) salt = _salt CACHED_CHAIN_ID = chain.id CACHED_DOMAIN_SEPARATOR = keccak256( _abi_encode( EIP712_TYPEHASH, NAME_HASH, VERSION_HASH, chain.id, self, salt, ) ) log Transfer(empty(address), self, 0) # <------- Fire empty transfer from # 0x0 to self for indexers to catch. # ------------------- Token transfers in and out of the AMM ------------------ @internal def _transfer_in( _coin_idx: uint256, _dx: uint256, sender: address, expect_optimistic_transfer: bool, ) -> uint256: """ @notice Transfers `_coin` from `sender` to `self` and calls `callback_sig` if it is not empty. @params _coin_idx uint256 Index of the coin to transfer in. @params dx amount of `_coin` to transfer into the pool. @params sender address to transfer `_coin` from. @params expect_optimistic_transfer bool True if pool expects user to transfer. This is only enabled for exchange_received. @return The amount of tokens received. """ coin_balance: uint256 = ERC20(coins[_coin_idx]).balanceOf(self) if expect_optimistic_transfer: # Only enabled in exchange_received: # it expects the caller of exchange_received to have sent tokens to # the pool before calling this method. # If someone donates extra tokens to the contract: do not acknowledge. # We only want to know if there are dx amount of tokens. Anything extra, # we ignore. This is why we need to check if received_amounts (which # accounts for coin balances of the contract) is atleast dx. # If we checked for received_amounts == dx, an extra transfer without a # call to exchange_received will break the method. dx: uint256 = coin_balance - self.balances[_coin_idx] assert dx >= _dx # dev: user didn't give us coins # Adjust balances self.balances[_coin_idx] += dx return dx # ----------------------------------------------- ERC20 transferFrom flow. # EXTERNAL CALL assert ERC20(coins[_coin_idx]).transferFrom( sender, self, _dx, default_return_value=True ) dx: uint256 = ERC20(coins[_coin_idx]).balanceOf(self) - coin_balance self.balances[_coin_idx] += dx return dx @internal def _transfer_out(_coin_idx: uint256, _amount: uint256, receiver: address): """ @notice Transfer a single token from the pool to receiver. @dev This function is called by `remove_liquidity` and `remove_liquidity_one`, `_claim_admin_fees` and `_exchange` methods. @params _coin_idx uint256 Index of the token to transfer out @params _amount Amount of token to transfer out @params receiver Address to send the tokens to """ # Adjust balances before handling transfers: self.balances[_coin_idx] -= _amount # EXTERNAL CALL assert ERC20(coins[_coin_idx]).transfer( receiver, _amount, default_return_value=True ) # -------------------------- AMM Main Functions ------------------------------ @external @nonreentrant("lock") def exchange( i: uint256, j: uint256, dx: uint256, min_dy: uint256, receiver: address = msg.sender ) -> uint256: """ @notice Exchange using wrapped native token by default @param i Index value for the input coin @param j Index value for the output coin @param dx Amount of input coin being swapped in @param min_dy Minimum amount of output coin to receive @param receiver Address to send the output coin to. Default is msg.sender @return uint256 Amount of tokens at index j received by the `receiver """ # _transfer_in updates self.balances here: dx_received: uint256 = self._transfer_in( i, dx, msg.sender, False ) # No ERC20 token transfers occur here: out: uint256[3] = self._exchange( i, j, dx_received, min_dy, ) # _transfer_out updates self.balances here. Update to state occurs before # external calls: self._transfer_out(j, out[0], receiver) # log: log TokenExchange(msg.sender, i, dx_received, j, out[0], out[1], out[2]) return out[0] @external @nonreentrant('lock') def exchange_received( i: uint256, j: uint256, dx: uint256, min_dy: uint256, receiver: address = msg.sender, ) -> uint256: """ @notice Exchange: but user must transfer dx amount of coin[i] tokens to pool first. Pool will not call transferFrom and will only check if a surplus of coins[i] is greater than or equal to `dx`. @dev Use-case is to reduce the number of redundant ERC20 token transfers in zaps. Primarily for dex-aggregators/arbitrageurs/searchers. Note for users: please transfer + exchange_received in 1 tx. @param i Index value for the input coin @param j Index value for the output coin @param dx Amount of input coin being swapped in @param min_dy Minimum amount of output coin to receive @param receiver Address to send the output coin to @return uint256 Amount of tokens at index j received by the `receiver` """ # _transfer_in updates self.balances here: dx_received: uint256 = self._transfer_in( i, dx, msg.sender, True # <---- expect_optimistic_transfer is set to True here. ) # No ERC20 token transfers occur here: out: uint256[3] = self._exchange( i, j, dx_received, min_dy, ) # _transfer_out updates self.balances here. Update to state occurs before # external calls: self._transfer_out(j, out[0], receiver) # log: log TokenExchange(msg.sender, i, dx_received, j, out[0], out[1], out[2]) return out[0] @external @nonreentrant("lock") def add_liquidity( amounts: uint256[N_COINS], min_mint_amount: uint256, receiver: address = msg.sender ) -> uint256: """ @notice Adds liquidity into the pool. @param amounts Amounts of each coin to add. @param min_mint_amount Minimum amount of LP to mint. @param receiver Address to send the LP tokens to. Default is msg.sender @return uint256 Amount of LP tokens received by the `receiver """ A_gamma: uint256[2] = self._A_gamma() xp: uint256[N_COINS] = self.balances amountsp: uint256[N_COINS] = empty(uint256[N_COINS]) d_token: uint256 = 0 d_token_fee: uint256 = 0 old_D: uint256 = 0 assert amounts[0] + amounts[1] > 0 # dev: no coins to add # --------------------- Get prices, balances ----------------------------- price_scale: uint256 = self.cached_price_scale # -------------------------------------- Update balances and calculate xp. xp_old: uint256[N_COINS] = xp amounts_received: uint256[N_COINS] = empty(uint256[N_COINS]) ########################## TRANSFER IN <------- for i in range(N_COINS): if amounts[i] > 0: # Updates self.balances here: amounts_received[i] = self._transfer_in( i, amounts[i], msg.sender, False, # <--------------------- Disable optimistic transfers. ) xp[i] = xp[i] + amounts_received[i] xp = [ xp[0] * PRECISIONS[0], unsafe_div(xp[1] * price_scale * PRECISIONS[1], PRECISION) ] xp_old = [ xp_old[0] * PRECISIONS[0], unsafe_div(xp_old[1] * price_scale * PRECISIONS[1], PRECISION) ] for i in range(N_COINS): if amounts_received[i] > 0: amountsp[i] = xp[i] - xp_old[i] # -------------------- Calculate LP tokens to mint ----------------------- if self.future_A_gamma_time > block.timestamp: # <--- A_gamma is ramping. # ----- Recalculate the invariant if A or gamma are undergoing a ramp. old_D = MATH.newton_D(A_gamma[0], A_gamma[1], xp_old, 0) else: old_D = self.D D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) token_supply: uint256 = self.totalSupply if old_D > 0: d_token = token_supply * D / old_D - token_supply else: d_token = self.get_xcp(D, price_scale) # <----- Making initial virtual price equal to 1. assert d_token > 0 # dev: nothing minted if old_D > 0: d_token_fee = ( self._calc_token_fee(amountsp, xp) * d_token / 10**10 + 1 ) d_token -= d_token_fee token_supply += d_token self.mint(receiver, d_token) self.admin_lp_virtual_balance += unsafe_div(ADMIN_FEE * d_token_fee, 10**10) price_scale = self.tweak_price(A_gamma, xp, D, 0) else: # (re)instatiating an empty pool: self.D = D self.virtual_price = 10**18 self.xcp_profit = 10**18 self.xcp_profit_a = 10**18 # Initialise xcp oracle here: self.cached_xcp_oracle = d_token # <--- virtual_price * totalSupply / 10**18 self.mint(receiver, d_token) assert d_token >= min_mint_amount, "Slippage" # ---------------------------------------------- Log and claim admin fees. log AddLiquidity( receiver, amounts_received, d_token_fee, token_supply, price_scale ) return d_token @external @nonreentrant("lock") def remove_liquidity( _amount: uint256, min_amounts: uint256[N_COINS], receiver: address = msg.sender, ) -> uint256[N_COINS]: """ @notice This withdrawal method is very safe, does no complex math since tokens are withdrawn in balanced proportions. No fees are charged. @param _amount Amount of LP tokens to burn @param min_amounts Minimum amounts of tokens to withdraw @param receiver Address to send the withdrawn tokens to @return uint256[3] Amount of pool tokens received by the `receiver` """ amount: uint256 = _amount balances: uint256[N_COINS] = self.balances withdraw_amounts: uint256[N_COINS] = empty(uint256[N_COINS]) # -------------------------------------------------------- Burn LP tokens. total_supply: uint256 = self.totalSupply # <------ Get totalSupply before self.burnFrom(msg.sender, _amount) # ---- reducing it with self.burnFrom. # There are two cases for withdrawing tokens from the pool. # Case 1. Withdrawal does not empty the pool. # In this situation, D is adjusted proportional to the amount of # LP tokens burnt. ERC20 tokens transferred is proportional # to : (AMM balance * LP tokens in) / LP token total supply # Case 2. Withdrawal empties the pool. # In this situation, all tokens are withdrawn and the invariant # is reset. if amount == total_supply: # <----------------------------------- Case 2. for i in range(N_COINS): withdraw_amounts[i] = balances[i] else: # <-------------------------------------------------------- Case 1. amount -= 1 # <---- To prevent rounding errors, favor LPs a tiny bit. for i in range(N_COINS): withdraw_amounts[i] = balances[i] * amount / total_supply assert withdraw_amounts[i] >= min_amounts[i] D: uint256 = self.D self.D = D - unsafe_div(D * amount, total_supply) # <----------- Reduce D # proportional to the amount of tokens leaving. Since withdrawals are # balanced, this is a simple subtraction. If amount == total_supply, # D will be 0. # ---------------------------------- Transfers --------------------------- for i in range(N_COINS): # _transfer_out updates self.balances here. Update to state occurs # before external calls: self._transfer_out(i, withdraw_amounts[i], receiver) log RemoveLiquidity(msg.sender, withdraw_amounts, total_supply - _amount) # --------------------------- Upkeep xcp oracle -------------------------- # Update xcp since liquidity was removed: xp: uint256[N_COINS] = self.xp(self.balances, self.cached_price_scale) last_xcp: uint256 = isqrt(xp[0] * xp[1]) # <----------- Cache it for now. last_timestamp: uint256[2] = self._unpack_2(self.last_timestamp) if last_timestamp[1] < block.timestamp: cached_xcp_oracle: uint256 = self.cached_xcp_oracle alpha: uint256 = MATH.wad_exp( -convert( unsafe_div( unsafe_sub(block.timestamp, last_timestamp[1]) * 10**18, self.xcp_ma_time # <---------- xcp ma time has is longer. ), int256, ) ) self.cached_xcp_oracle = unsafe_div( last_xcp * (10**18 - alpha) + cached_xcp_oracle * alpha, 10**18 ) last_timestamp[1] = block.timestamp # Pack and store timestamps: self.last_timestamp = self._pack_2(last_timestamp[0], last_timestamp[1]) # Store last xcp self.last_xcp = last_xcp return withdraw_amounts @external @nonreentrant("lock") def remove_liquidity_one_coin( token_amount: uint256, i: uint256, min_amount: uint256, receiver: address = msg.sender ) -> uint256: """ @notice Withdraw liquidity in a single token. Involves fees (lower than swap fees). @dev This operation also involves an admin fee claim. @param token_amount Amount of LP tokens to burn @param i Index of the token to withdraw @param min_amount Minimum amount of token to withdraw. @param receiver Address to send the withdrawn tokens to @return Amount of tokens at index i received by the `receiver` """ self._claim_admin_fees() # <--------- Auto-claim admin fees occasionally. A_gamma: uint256[2] = self._A_gamma() dy: uint256 = 0 D: uint256 = 0 p: uint256 = 0 xp: uint256[N_COINS] = empty(uint256[N_COINS]) approx_fee: uint256 = 0 # ------------------------------------------------------------------------ dy, D, xp, approx_fee = self._calc_withdraw_one_coin( A_gamma, token_amount, i, (self.future_A_gamma_time > block.timestamp), # <------- During ramps ) # we need to update D. assert dy >= min_amount, "Slippage" # ---------------------------- State Updates ----------------------------- # Burn user's tokens: self.burnFrom(msg.sender, token_amount) packed_price_scale: uint256 = self.tweak_price(A_gamma, xp, D, 0) # Safe to use D from _calc_withdraw_one_coin here ---^ # ------------------------- Transfers ------------------------------------ # _transfer_out updates self.balances here. Update to state occurs before # external calls: self._transfer_out(i, dy, receiver) log RemoveLiquidityOne( msg.sender, token_amount, i, dy, approx_fee, packed_price_scale ) return dy # -------------------------- Packing functions ------------------------------- @internal @pure def _pack_3(x: uint256[3]) -> uint256: """ @notice Packs 3 integers with values <= 10**18 into a uint256 @param x The uint256[3] to pack @return uint256 Integer with packed values """ return (x[0] << 128) | (x[1] << 64) | x[2] @internal @pure def _unpack_3(_packed: uint256) -> uint256[3]: """ @notice Unpacks a uint256 into 3 integers (values must be <= 10**18) @param val The uint256 to unpack @return uint256[3] A list of length 3 with unpacked integers """ return [ (_packed >> 128) & 18446744073709551615, (_packed >> 64) & 18446744073709551615, _packed & 18446744073709551615, ] @pure @internal def _pack_2(p1: uint256, p2: uint256) -> uint256: return p1 | (p2 << 128) @pure @internal def _unpack_2(packed: uint256) -> uint256[2]: return [packed & (2**128 - 1), packed >> 128] # ---------------------- AMM Internal Functions ------------------------------- @internal def _exchange( i: uint256, j: uint256, dx_received: uint256, min_dy: uint256, ) -> uint256[3]: assert i != j # dev: coin index out of range assert dx_received > 0 # dev: do not exchange 0 coins A_gamma: uint256[2] = self._A_gamma() xp: uint256[N_COINS] = self.balances dy: uint256 = 0 y: uint256 = xp[j] x0: uint256 = xp[i] - dx_received # old xp[i] price_scale: uint256 = self.cached_price_scale xp = [ xp[0] * PRECISIONS[0], unsafe_div(xp[1] * price_scale * PRECISIONS[1], PRECISION) ] # ----------- Update invariant if A, gamma are undergoing ramps --------- t: uint256 = self.future_A_gamma_time if t > block.timestamp: x0 *= PRECISIONS[i] if i > 0: x0 = unsafe_div(x0 * price_scale, PRECISION) x1: uint256 = xp[i] # <------------------ Back up old value in xp ... xp[i] = x0 # | self.D = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) # | xp[i] = x1 # <-------------------------------------- ... and restore. # ----------------------- Calculate dy and fees -------------------------- D: uint256 = self.D y_out: uint256[2] = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, j) dy = xp[j] - y_out[0] xp[j] -= dy dy -= 1 if j > 0: dy = dy * PRECISION / price_scale dy /= PRECISIONS[j] fee: uint256 = unsafe_div(self._fee(xp) * dy, 10**10) dy -= fee # <--------------------- Subtract fee from the outgoing amount. assert dy >= min_dy, "Slippage" y -= dy y *= PRECISIONS[j] if j > 0: y = unsafe_div(y * price_scale, PRECISION) xp[j] = y # <------------------------------------------------- Update xp. # ------ Tweak price_scale with good initial guess for newton_D ---------- price_scale = self.tweak_price(A_gamma, xp, 0, y_out[1]) return [dy, fee, price_scale] @internal def tweak_price( A_gamma: uint256[2], _xp: uint256[N_COINS], new_D: uint256, K0_prev: uint256 = 0, ) -> uint256: """ @notice Updates price_oracle, last_price and conditionally adjusts price_scale. This is called whenever there is an unbalanced liquidity operation: _exchange, add_liquidity, or remove_liquidity_one_coin. @dev Contains main liquidity rebalancing logic, by tweaking `price_scale`. @param A_gamma Array of A and gamma parameters. @param _xp Array of current balances. @param new_D New D value. @param K0_prev Initial guess for `newton_D`. """ # ---------------------------- Read storage ------------------------------ price_oracle: uint256 = self.cached_price_oracle last_prices: uint256 = self.last_prices price_scale: uint256 = self.cached_price_scale rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params) # Contains: allowed_extra_profit, adjustment_step, ma_time. -----^ total_supply: uint256 = self.totalSupply old_xcp_profit: uint256 = self.xcp_profit old_virtual_price: uint256 = self.virtual_price # ----------------------- Update Oracles if needed ----------------------- last_timestamp: uint256[2] = self._unpack_2(self.last_timestamp) alpha: uint256 = 0 if last_timestamp[0] < block.timestamp: # 0th index is for price_oracle. # The moving average price oracle is calculated using the last_price # of the trade at the previous block, and the price oracle logged # before that trade. This can happen only once per block. # ------------------ Calculate moving average params ----------------- alpha = MATH.wad_exp( -convert( unsafe_div( unsafe_sub(block.timestamp, last_timestamp[0]) * 10**18, rebalancing_params[2] # <----------------------- ma_time. ), int256, ) ) # ---------------------------------------------- Update price oracles. # ----------------- We cap state price that goes into the EMA with # 2 x price_scale. price_oracle = unsafe_div( min(last_prices, 2 * price_scale) * (10**18 - alpha) + price_oracle * alpha, # ^-------- Cap spot price into EMA. 10**18 ) self.cached_price_oracle = price_oracle last_timestamp[0] = block.timestamp # ----------------------------------------------------- Update xcp oracle. if last_timestamp[1] < block.timestamp: cached_xcp_oracle: uint256 = self.cached_xcp_oracle alpha = MATH.wad_exp( -convert( unsafe_div( unsafe_sub(block.timestamp, last_timestamp[1]) * 10**18, self.xcp_ma_time # <---------- xcp ma time has is longer. ), int256, ) ) self.cached_xcp_oracle = unsafe_div( self.last_xcp * (10**18 - alpha) + cached_xcp_oracle * alpha, 10**18 ) # Pack and store timestamps: last_timestamp[1] = block.timestamp self.last_timestamp = self._pack_2(last_timestamp[0], last_timestamp[1]) # `price_oracle` is used further on to calculate its vector distance from # price_scale. This distance is used to calculate the amount of adjustment # to be done to the price_scale. # ------------------------------------------------------------------------ # ------------------ If new_D is set to 0, calculate it ------------------ D_unadjusted: uint256 = new_D if new_D == 0: # <--------------------------- _exchange sets new_D to 0. D_unadjusted = MATH.newton_D(A_gamma[0], A_gamma[1], _xp, K0_prev) # ----------------------- Calculate last_prices -------------------------- self.last_prices = unsafe_div( MATH.get_p(_xp, D_unadjusted, A_gamma) * price_scale, 10**18 ) # ---------- Update profit numbers without price adjustment first -------- xp: uint256[N_COINS] = [ unsafe_div(D_unadjusted, N_COINS), D_unadjusted * PRECISION / (N_COINS * price_scale) # <------ safediv. ] # with price_scale. xcp_profit: uint256 = 10**18 virtual_price: uint256 = 10**18 if old_virtual_price > 0: xcp: uint256 = isqrt(xp[0] * xp[1]) virtual_price = 10**18 * xcp / total_supply xcp_profit = unsafe_div( old_xcp_profit * virtual_price, old_virtual_price ) # <---------------- Safu to do unsafe_div as old_virtual_price > 0. # If A and gamma are not undergoing ramps (t < block.timestamp), # ensure new virtual_price is not less than old virtual_price, # else the pool suffers a loss. if self.future_A_gamma_time < block.timestamp: assert virtual_price > old_virtual_price, "Loss" # -------------------------- Cache last_xcp -------------------------- self.last_xcp = xcp # geometric_mean(D * price_scale) self.xcp_profit = xcp_profit # ------------ Rebalance liquidity if there's enough profits to adjust it: if virtual_price * 2 - 10**18 > xcp_profit + 2 * rebalancing_params[0]: # allowed_extra_profit --------^ # ------------------- Get adjustment step ---------------------------- # Calculate the vector distance between price_scale and # price_oracle. norm: uint256 = unsafe_div( unsafe_mul(price_oracle, 10**18), price_scale ) if norm > 10**18: norm = unsafe_sub(norm, 10**18) else: norm = unsafe_sub(10**18, norm) adjustment_step: uint256 = max( rebalancing_params[1], unsafe_div(norm, 5) ) # ^------------------------------------- adjustment_step. if norm > adjustment_step: # <---------- We only adjust prices if the # vector distance between price_oracle and price_scale is # large enough. This check ensures that no rebalancing # occurs if the distance is low i.e. the pool prices are # pegged to the oracle prices. # ------------------------------------- Calculate new price scale. p_new: uint256 = unsafe_div( price_scale * unsafe_sub(norm, adjustment_step) + adjustment_step * price_oracle, norm ) # <---- norm is non-zero and gt adjustment_step; unsafe = safe. # ---------------- Update stale xp (using price_scale) with p_new. xp = [ _xp[0], unsafe_div(_xp[1] * p_new, price_scale) ] # ------------------------------------------ Update D with new xp. D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) for k in range(N_COINS): frac: uint256 = xp[k] * 10**18 / D # <----- Check validity of assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # p_new. # ------------------------------------- Convert xp to real prices. xp = [ unsafe_div(D, N_COINS), D * PRECISION / (N_COINS * p_new) ] # ---------- Calculate new virtual_price using new xp and D. Reuse # `old_virtual_price` (but it has new virtual_price). old_virtual_price = unsafe_div( 10**18 * isqrt(xp[0] * xp[1]), total_supply ) # <----- unsafe_div because we did safediv before (if vp>1e18) # ---------------------------- Proceed if we've got enough profit. if ( old_virtual_price > 10**18 and 2 * old_virtual_price - 10**18 > xcp_profit ): self.D = D self.virtual_price = old_virtual_price self.cached_price_scale = p_new return p_new # --------- price_scale was not adjusted. Update the profit counter and D. self.D = D_unadjusted self.virtual_price = virtual_price return price_scale @internal def _claim_admin_fees(): """ @notice Claims admin fees and sends it to fee_receiver set in the factory. @dev Functionally similar to: 1. Calculating admin's share of fees, 2. minting LP tokens, 3. admin claims underlying tokens via remove_liquidity. """ # --------------------- Check if fees can be claimed --------------------- # Disable fee claiming if: # 1. If time passed since last fee claim is less than # MIN_ADMIN_FEE_CLAIM_INTERVAL. # 2. Pool parameters are being ramped. last_claim_time: uint256 = self.last_admin_fee_claim_timestamp if ( unsafe_sub(block.timestamp, last_claim_time) < MIN_ADMIN_FEE_CLAIM_INTERVAL or self.future_A_gamma_time > block.timestamp ): return xcp_profit: uint256 = self.xcp_profit # <---------- Current pool profits. xcp_profit_a: uint256 = self.xcp_profit_a # <- Profits at previous claim. current_lp_token_supply: uint256 = self.totalSupply # Do not claim admin fees if: # 1. insufficient profits accrued since last claim, and # 2. there are less than 10**18 (or 1 unit of) lp tokens, else it can lead # to manipulated virtual prices. if xcp_profit <= xcp_profit_a or current_lp_token_supply < 10**18: return # ---------- Conditions met to claim admin fees: compute state. ---------- A_gamma: uint256[2] = self._A_gamma() D: uint256 = self.D vprice: uint256 = self.virtual_price price_scale: uint256 = self.cached_price_scale fee_receiver: address = factory.fee_receiver() balances: uint256[N_COINS] = self.balances # Admin fees are calculated as follows. # 1. Calculate accrued profit since last claim. `xcp_profit` # is the current profits. `xcp_profit_a` is the profits # at the previous claim. # 2. Take out admin's share, which is hardcoded at 5 * 10**9. # (50% => half of 100% => 10**10 / 2 => 5 * 10**9). # 3. Since half of the profits go to rebalancing the pool, we # are left with half; so divide by 2. fees: uint256 = unsafe_div( unsafe_sub(xcp_profit, xcp_profit_a) * ADMIN_FEE, 2 * 10**10 ) # ------------------------------ Claim admin fees by minting admin's share # of the pool in LP tokens. # This is the admin fee tokens claimed in self.add_liquidity. We add it to # the LP token share that the admin needs to claim: admin_share: uint256 = self.admin_lp_virtual_balance frac: uint256 = 0 if fee_receiver != empty(address) and fees > 0: # -------------------------------- Calculate admin share to be minted. frac = vprice * 10**18 / (vprice - fees) - 10**18 admin_share += current_lp_token_supply * frac / 10**18 # ------ Subtract fees from profits that will be used for rebalancing. xcp_profit -= fees * 2 # ------------------- Recalculate virtual_price following admin fee claim. total_supply_including_admin_share: uint256 = ( current_lp_token_supply + admin_share ) vprice = ( 10**18 * self.get_xcp(D, price_scale) / total_supply_including_admin_share ) # Do not claim fees if doing so causes virtual price to drop below 10**18. if vprice < 10**18: return # ---------------------------- Update State ------------------------------ # Set admin virtual LP balances to zero because we claimed: self.admin_lp_virtual_balance = 0 self.xcp_profit = xcp_profit self.last_admin_fee_claim_timestamp = block.timestamp # Since we reduce balances: virtual price goes down self.virtual_price = vprice # Adjust D after admin seemingly removes liquidity self.D = D - unsafe_div(D * admin_share, total_supply_including_admin_share) if xcp_profit > xcp_profit_a: self.xcp_profit_a = xcp_profit # <-------- Cache last claimed profit. # --------------------------- Handle Transfers --------------------------- admin_tokens: uint256[N_COINS] = empty(uint256[N_COINS]) if admin_share > 0: for i in range(N_COINS): admin_tokens[i] = ( balances[i] * admin_share / total_supply_including_admin_share ) # _transfer_out tokens to admin and update self.balances. State # update to self.balances occurs before external contract calls: self._transfer_out(i, admin_tokens[i], fee_receiver) log ClaimAdminFee(fee_receiver, admin_tokens) @internal @pure def xp( balances: uint256[N_COINS], price_scale: uint256, ) -> uint256[N_COINS]: return [ balances[0] * PRECISIONS[0], unsafe_div(balances[1] * PRECISIONS[1] * price_scale, PRECISION) ] @view @internal def _A_gamma() -> uint256[2]: t1: uint256 = self.future_A_gamma_time A_gamma_1: uint256 = self.future_A_gamma gamma1: uint256 = A_gamma_1 & 2**128 - 1 A1: uint256 = A_gamma_1 >> 128 if block.timestamp < t1: # --------------- Handle ramping up and down of A -------------------- A_gamma_0: uint256 = self.initial_A_gamma t0: uint256 = self.initial_A_gamma_time t1 -= t0 t0 = block.timestamp - t0 t2: uint256 = t1 - t0 A1 = ((A_gamma_0 >> 128) * t2 + A1 * t0) / t1 gamma1 = ((A_gamma_0 & 2**128 - 1) * t2 + gamma1 * t0) / t1 return [A1, gamma1] @internal @view def _fee(xp: uint256[N_COINS]) -> uint256: fee_params: uint256[3] = self._unpack_3(self.packed_fee_params) f: uint256 = xp[0] + xp[1] f = fee_params[2] * 10**18 / ( fee_params[2] + 10**18 - (10**18 * N_COINS**N_COINS) * xp[0] / f * xp[1] / f ) return unsafe_div( fee_params[0] * f + fee_params[1] * (10**18 - f), 10**18 ) @internal @pure def get_xcp(D: uint256, price_scale: uint256) -> uint256: x: uint256[N_COINS] = [ unsafe_div(D, N_COINS), D * PRECISION / (price_scale * N_COINS) ] return isqrt(x[0] * x[1]) # <------------------- Geometric Mean. @view @internal def _calc_token_fee(amounts: uint256[N_COINS], xp: uint256[N_COINS]) -> uint256: # fee = sum(amounts_i - avg(amounts)) * fee' / sum(amounts) fee: uint256 = unsafe_div( unsafe_mul(self._fee(xp), N_COINS), unsafe_mul(4, unsafe_sub(N_COINS, 1)) ) S: uint256 = 0 for _x in amounts: S += _x avg: uint256 = unsafe_div(S, N_COINS) Sdiff: uint256 = 0 for _x in amounts: if _x > avg: Sdiff += unsafe_sub(_x, avg) else: Sdiff += unsafe_sub(avg, _x) return fee * Sdiff / S + NOISE_FEE @internal @view def _calc_withdraw_one_coin( A_gamma: uint256[2], token_amount: uint256, i: uint256, update_D: bool, ) -> (uint256, uint256, uint256[N_COINS], uint256): token_supply: uint256 = self.totalSupply assert token_amount <= token_supply # dev: token amount more than supply assert i < N_COINS # dev: coin out of range xx: uint256[N_COINS] = self.balances D0: uint256 = 0 # -------------------------- Calculate D0 and xp ------------------------- price_scale_i: uint256 = self.cached_price_scale * PRECISIONS[1] xp: uint256[N_COINS] = [ xx[0] * PRECISIONS[0], unsafe_div(xx[1] * price_scale_i, PRECISION) ] if i == 0: price_scale_i = PRECISION * PRECISIONS[0] if update_D: # <-------------- D is updated if pool is undergoing a ramp. D0 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) else: D0 = self.D D: uint256 = D0 # -------------------------------- Fee Calc ------------------------------ # Charge fees on D. Roughly calculate xp[i] after withdrawal and use that # to calculate fee. Precision is not paramount here: we just want a # behavior where the higher the imbalance caused the more fee the AMM # charges. # xp is adjusted assuming xp[0] ~= xp[1] ~= x[2], which is usually not the # case. We charge self._fee(xp), where xp is an imprecise adjustment post # withdrawal in one coin. If the withdraw is too large: charge max fee by # default. This is because the fee calculation will otherwise underflow. xp_imprecise: uint256[N_COINS] = xp xp_correction: uint256 = xp[i] * N_COINS * token_amount / token_supply fee: uint256 = self._unpack_3(self.packed_fee_params)[1] # <- self.out_fee. if xp_correction < xp_imprecise[i]: xp_imprecise[i] -= xp_correction fee = self._fee(xp_imprecise) dD: uint256 = unsafe_div(token_amount * D, token_supply) D_fee: uint256 = fee * dD / (2 * 10**10) + 1 # <------- Actual fee on D. # --------- Calculate `approx_fee` (assuming balanced state) in ith token. # -------------------------------- We only need this for fee in the event. approx_fee: uint256 = N_COINS * D_fee * xx[i] / D # <------------------<---------- TODO: Check math. # ------------------------------------------------------------------------ D -= (dD - D_fee) # <----------------------------------- Charge fee on D. # --------------------------------- Calculate `y_out`` with `(D - D_fee)`. y: uint256 = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, i)[0] dy: uint256 = (xp[i] - y) * PRECISION / price_scale_i xp[i] = y return dy, D, xp, approx_fee # ------------------------ ERC20 functions ----------------------------------- @internal def _approve(_owner: address, _spender: address, _value: uint256): self.allowance[_owner][_spender] = _value log Approval(_owner, _spender, _value) @internal def _transfer(_from: address, _to: address, _value: uint256): assert _to not in [self, empty(address)] self.balanceOf[_from] -= _value self.balanceOf[_to] += _value log Transfer(_from, _to, _value) @view @internal def _domain_separator() -> bytes32: if chain.id != CACHED_CHAIN_ID: return keccak256( _abi_encode( EIP712_TYPEHASH, NAME_HASH, VERSION_HASH, chain.id, self, salt, ) ) return CACHED_DOMAIN_SEPARATOR @external def transferFrom(_from: address, _to: address, _value: uint256) -> bool: """ @dev Transfer tokens from one address to another. @param _from address The address which you want to send tokens from @param _to address The address which you want to transfer to @param _value uint256 the amount of tokens to be transferred @return bool True on successul transfer. Reverts otherwise. """ _allowance: uint256 = self.allowance[_from][msg.sender] if _allowance != max_value(uint256): self._approve(_from, msg.sender, _allowance - _value) self._transfer(_from, _to, _value) return True @external def transfer(_to: address, _value: uint256) -> bool: """ @dev Transfer token for a specified address @param _to The address to transfer to. @param _value The amount to be transferred. @return bool True on successful transfer. Reverts otherwise. """ self._transfer(msg.sender, _to, _value) return True @external def approve(_spender: address, _value: uint256) -> bool: """ @notice Allow `_spender` to transfer up to `_value` amount of tokens from the caller's account. @param _spender The account permitted to spend up to `_value` amount of caller's funds. @param _value The amount of tokens `_spender` is allowed to spend. @return bool Success """ self._approve(msg.sender, _spender, _value) return True @external def permit( _owner: address, _spender: address, _value: uint256, _deadline: uint256, _v: uint8, _r: bytes32, _s: bytes32, ) -> bool: """ @notice Permit `_spender` to spend up to `_value` amount of `_owner`'s tokens via a signature. @dev In the event of a chain fork, replay attacks are prevented as domain separator is recalculated. However, this is only if the resulting chains update their chainId. @param _owner The account which generated the signature and is granting an allowance. @param _spender The account which will be granted an allowance. @param _value The approval amount. @param _deadline The deadline by which the signature must be submitted. @param _v The last byte of the ECDSA signature. @param _r The first 32 bytes of the ECDSA signature. @param _s The second 32 bytes of the ECDSA signature. @return bool Success. """ assert _owner != empty(address) # dev: invalid owner assert block.timestamp <= _deadline # dev: permit expired nonce: uint256 = self.nonces[_owner] digest: bytes32 = keccak256( concat( b"\x19\x01", self._domain_separator(), keccak256( _abi_encode( EIP2612_TYPEHASH, _owner, _spender, _value, nonce, _deadline ) ), ) ) assert ecrecover(digest, _v, _r, _s) == _owner # dev: invalid signature self.nonces[_owner] = unsafe_add(nonce, 1) # <-- Unsafe add is safe here. self._approve(_owner, _spender, _value) return True @internal def mint(_to: address, _value: uint256) -> bool: """ @dev Mint an amount of the token and assigns it to an account. This encapsulates the modification of balances such that the proper events are emitted. @param _to The account that will receive the created tokens. @param _value The amount that will be created. @return bool Success. """ self.totalSupply += _value self.balanceOf[_to] += _value log Transfer(empty(address), _to, _value) return True @internal def burnFrom(_to: address, _value: uint256) -> bool: """ @dev Burn an amount of the token from a given account. @param _to The account whose tokens will be burned. @param _value The amount that will be burned. @return bool Success. """ self.totalSupply -= _value self.balanceOf[_to] -= _value log Transfer(_to, empty(address), _value) return True # ------------------------- AMM View Functions ------------------------------- @internal @view def internal_price_oracle() -> uint256: """ @notice Returns the oracle price of the coin at index `k` w.r.t the coin at index 0. @dev The oracle is an exponential moving average, with a periodicity determined by `self.ma_time`. The aggregated prices are cached state prices (dy/dx) calculated AFTER the latest trade. @param k The index of the coin. @return uint256 Price oracle value of kth coin. """ price_oracle: uint256 = self.cached_price_oracle price_scale: uint256 = self.cached_price_scale last_prices_timestamp: uint256 = self._unpack_2(self.last_timestamp)[0] if last_prices_timestamp < block.timestamp: # <------------ Update moving # average if needed. last_prices: uint256 = self.last_prices ma_time: uint256 = self._unpack_3(self.packed_rebalancing_params)[2] alpha: uint256 = MATH.wad_exp( -convert( unsafe_sub(block.timestamp, last_prices_timestamp) * 10**18 / ma_time, int256, ) ) # ---- We cap state price that goes into the EMA with 2 x price_scale. return ( min(last_prices, 2 * price_scale) * (10**18 - alpha) + price_oracle * alpha ) / 10**18 return price_oracle @external @view def fee_receiver() -> address: """ @notice Returns the address of the admin fee receiver. @return address Fee receiver. """ return factory.fee_receiver() @external @view def admin() -> address: """ @notice Returns the address of the pool's admin. @return address Admin. """ return factory.admin() @external @view def calc_token_amount(amounts: uint256[N_COINS], deposit: bool) -> uint256: """ @notice Calculate LP tokens minted or to be burned for depositing or removing `amounts` of coins @dev Includes fee. @param amounts Amounts of tokens being deposited or withdrawn @param deposit True if it is a deposit action, False if withdrawn. @return uint256 Amount of LP tokens deposited or withdrawn. """ view_contract: address = factory.views_implementation() return Views(view_contract).calc_token_amount(amounts, deposit, self) @external @view def get_dy(i: uint256, j: uint256, dx: uint256) -> uint256: """ @notice Get amount of coin[j] tokens received for swapping in dx amount of coin[i] @dev Includes fee. @param i index of input token. Check pool.coins(i) to get coin address at ith index @param j index of output token @param dx amount of input coin[i] tokens @return uint256 Exact amount of output j tokens for dx amount of i input tokens. """ view_contract: address = factory.views_implementation() return Views(view_contract).get_dy(i, j, dx, self) @external @view def get_dx(i: uint256, j: uint256, dy: uint256) -> uint256: """ @notice Get amount of coin[i] tokens to input for swapping out dy amount of coin[j] @dev This is an approximate method, and returns estimates close to the input amount. Expensive to call on-chain. @param i index of input token. Check pool.coins(i) to get coin address at ith index @param j index of output token @param dy amount of input coin[j] tokens received @return uint256 Approximate amount of input i tokens to get dy amount of j tokens. """ view_contract: address = factory.views_implementation() return Views(view_contract).get_dx(i, j, dy, self) @external @view @nonreentrant("lock") def lp_price() -> uint256: """ @notice Calculates the current price of the LP token w.r.t coin at the 0th index @return uint256 LP price. """ return 2 * self.virtual_price * isqrt(self.internal_price_oracle() * 10**18) / 10**18 @external @view @nonreentrant("lock") def get_virtual_price() -> uint256: """ @notice Calculates the current virtual price of the pool LP token. @dev Not to be confused with `self.virtual_price` which is a cached virtual price. @return uint256 Virtual Price. """ return 10**18 * self.get_xcp(self.D, self.cached_price_scale) / self.totalSupply @external @view @nonreentrant("lock") def price_oracle() -> uint256: """ @notice Returns the oracle price of the coin at index `k` w.r.t the coin at index 0. @dev The oracle is an exponential moving average, with a periodicity determined by `self.ma_time`. The aggregated prices are cached state prices (dy/dx) calculated AFTER the latest trade. @return uint256 Price oracle value of kth coin. """ return self.internal_price_oracle() @external @view @nonreentrant("lock") def xcp_oracle() -> uint256: """ @notice Returns the oracle value for xcp. @dev The oracle is an exponential moving average, with a periodicity determined by `self.xcp_ma_time`. `TVL` is xcp, calculated as either: 1. virtual_price * total_supply, OR 2. self.get_xcp(...), OR 3. MATH.geometric_mean(xp) @return uint256 Oracle value of xcp. """ last_prices_timestamp: uint256 = self._unpack_2(self.last_timestamp)[1] cached_xcp_oracle: uint256 = self.cached_xcp_oracle if last_prices_timestamp < block.timestamp: alpha: uint256 = MATH.wad_exp( -convert( unsafe_div( unsafe_sub(block.timestamp, last_prices_timestamp) * 10**18, self.xcp_ma_time ), int256, ) ) return (self.last_xcp * (10**18 - alpha) + cached_xcp_oracle * alpha) / 10**18 return cached_xcp_oracle @external @view @nonreentrant("lock") def price_scale() -> uint256: """ @notice Returns the price scale of the coin at index `k` w.r.t the coin at index 0. @dev Price scale determines the price band around which liquidity is concentrated. @return uint256 Price scale of coin. """ return self.cached_price_scale @external @view def fee() -> uint256: """ @notice Returns the fee charged by the pool at current state. @dev Not to be confused with the fee charged at liquidity action, since there the fee is calculated on `xp` AFTER liquidity is added or removed. @return uint256 fee bps. """ return self._fee(self.xp(self.balances, self.cached_price_scale)) @view @external def calc_withdraw_one_coin(token_amount: uint256, i: uint256) -> uint256: """ @notice Calculates output tokens with fee @param token_amount LP Token amount to burn @param i token in which liquidity is withdrawn @return uint256 Amount of ith tokens received for burning token_amount LP tokens. """ return self._calc_withdraw_one_coin( self._A_gamma(), token_amount, i, (self.future_A_gamma_time > block.timestamp) )[0] @external @view def calc_token_fee( amounts: uint256[N_COINS], xp: uint256[N_COINS] ) -> uint256: """ @notice Returns the fee charged on the given amounts for add_liquidity. @param amounts The amounts of coins being added to the pool. @param xp The current balances of the pool multiplied by coin precisions. @return uint256 Fee charged. """ return self._calc_token_fee(amounts, xp) @view @external def A() -> uint256: """ @notice Returns the current pool amplification parameter. @return uint256 A param. """ return self._A_gamma()[0] @view @external def gamma() -> uint256: """ @notice Returns the current pool gamma parameter. @return uint256 gamma param. """ return self._A_gamma()[1] @view @external def mid_fee() -> uint256: """ @notice Returns the current mid fee @return uint256 mid_fee value. """ return self._unpack_3(self.packed_fee_params)[0] @view @external def out_fee() -> uint256: """ @notice Returns the current out fee @return uint256 out_fee value. """ return self._unpack_3(self.packed_fee_params)[1] @view @external def fee_gamma() -> uint256: """ @notice Returns the current fee gamma @return uint256 fee_gamma value. """ return self._unpack_3(self.packed_fee_params)[2] @view @external def allowed_extra_profit() -> uint256: """ @notice Returns the current allowed extra profit @return uint256 allowed_extra_profit value. """ return self._unpack_3(self.packed_rebalancing_params)[0] @view @external def adjustment_step() -> uint256: """ @notice Returns the current adjustment step @return uint256 adjustment_step value. """ return self._unpack_3(self.packed_rebalancing_params)[1] @view @external def ma_time() -> uint256: """ @notice Returns the current moving average time in seconds @dev To get time in seconds, the parameter is multipled by ln(2) One can expect off-by-one errors here. @return uint256 ma_time value. """ return self._unpack_3(self.packed_rebalancing_params)[2] * 694 / 1000 @view @external def precisions() -> uint256[N_COINS]: # <-------------- For by view contract. """ @notice Returns the precisions of each coin in the pool. @return uint256[3] precisions of coins. """ return PRECISIONS @external @view def fee_calc(xp: uint256[N_COINS]) -> uint256: # <----- For by view contract. """ @notice Returns the fee charged by the pool at current state. @param xp The current balances of the pool multiplied by coin precisions. @return uint256 Fee value. """ return self._fee(xp) @view @external def DOMAIN_SEPARATOR() -> bytes32: """ @notice EIP712 domain separator. @return bytes32 Domain Separator set for the current chain. """ return self._domain_separator() # ------------------------- AMM Admin Functions ------------------------------ @external def ramp_A_gamma( future_A: uint256, future_gamma: uint256, future_time: uint256 ): """ @notice Initialise Ramping A and gamma parameter values linearly. @dev Only accessible by factory admin, and only @param future_A The future A value. @param future_gamma The future gamma value. @param future_time The timestamp at which the ramping will end. """ assert msg.sender == factory.admin() # dev: only owner assert block.timestamp > self.initial_A_gamma_time + (MIN_RAMP_TIME - 1) # dev: ramp undergoing assert future_time > block.timestamp + MIN_RAMP_TIME - 1 # dev: insufficient time A_gamma: uint256[2] = self._A_gamma() initial_A_gamma: uint256 = A_gamma[0] << 128 initial_A_gamma = initial_A_gamma | A_gamma[1] assert future_A > MIN_A - 1 assert future_A < MAX_A + 1 assert future_gamma > MIN_GAMMA - 1 assert future_gamma < MAX_GAMMA + 1 ratio: uint256 = 10**18 * future_A / A_gamma[0] assert ratio < 10**18 * MAX_A_CHANGE + 1 assert ratio > 10**18 / MAX_A_CHANGE - 1 ratio = 10**18 * future_gamma / A_gamma[1] assert ratio < 10**18 * MAX_A_CHANGE + 1 assert ratio > 10**18 / MAX_A_CHANGE - 1 self.initial_A_gamma = initial_A_gamma self.initial_A_gamma_time = block.timestamp future_A_gamma: uint256 = future_A << 128 future_A_gamma = future_A_gamma | future_gamma self.future_A_gamma_time = future_time self.future_A_gamma = future_A_gamma log RampAgamma( A_gamma[0], future_A, A_gamma[1], future_gamma, block.timestamp, future_time, ) @external def stop_ramp_A_gamma(): """ @notice Stop Ramping A and gamma parameters immediately. @dev Only accessible by factory admin. """ assert msg.sender == factory.admin() # dev: only owner A_gamma: uint256[2] = self._A_gamma() current_A_gamma: uint256 = A_gamma[0] << 128 current_A_gamma = current_A_gamma | A_gamma[1] self.initial_A_gamma = current_A_gamma self.future_A_gamma = current_A_gamma self.initial_A_gamma_time = block.timestamp self.future_A_gamma_time = block.timestamp # ------ Now (block.timestamp < t1) is always False, so we return saved A. log StopRampA(A_gamma[0], A_gamma[1], block.timestamp) @external @nonreentrant('lock') def apply_new_parameters( _new_mid_fee: uint256, _new_out_fee: uint256, _new_fee_gamma: uint256, _new_allowed_extra_profit: uint256, _new_adjustment_step: uint256, _new_ma_time: uint256, _new_xcp_ma_time: uint256, ): """ @notice Commit new parameters. @dev Only accessible by factory admin. @param _new_mid_fee The new mid fee. @param _new_out_fee The new out fee. @param _new_fee_gamma The new fee gamma. @param _new_allowed_extra_profit The new allowed extra profit. @param _new_adjustment_step The new adjustment step. @param _new_ma_time The new ma time. ma_time is time_in_seconds/ln(2). @param _new_xcp_ma_time The new ma time for xcp oracle. """ assert msg.sender == factory.admin() # dev: only owner # ----------------------------- Set fee params --------------------------- new_mid_fee: uint256 = _new_mid_fee new_out_fee: uint256 = _new_out_fee new_fee_gamma: uint256 = _new_fee_gamma current_fee_params: uint256[3] = self._unpack_3(self.packed_fee_params) if new_out_fee < MAX_FEE + 1: assert new_out_fee > MIN_FEE - 1 # dev: fee is out of range else: new_out_fee = current_fee_params[1] if new_mid_fee > MAX_FEE: new_mid_fee = current_fee_params[0] assert new_mid_fee <= new_out_fee # dev: mid-fee is too high if new_fee_gamma < 10**18: assert new_fee_gamma > 0 # dev: fee_gamma out of range [1 .. 10**18] else: new_fee_gamma = current_fee_params[2] self.packed_fee_params = self._pack_3([new_mid_fee, new_out_fee, new_fee_gamma]) # ----------------- Set liquidity rebalancing parameters ----------------- new_allowed_extra_profit: uint256 = _new_allowed_extra_profit new_adjustment_step: uint256 = _new_adjustment_step new_ma_time: uint256 = _new_ma_time current_rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params) if new_allowed_extra_profit > 10**18: new_allowed_extra_profit = current_rebalancing_params[0] if new_adjustment_step > 10**18: new_adjustment_step = current_rebalancing_params[1] if new_ma_time < 872542: # <----- Calculated as: 7 * 24 * 60 * 60 / ln(2) assert new_ma_time > 86 # dev: MA time should be longer than 60/ln(2) else: new_ma_time = current_rebalancing_params[2] self.packed_rebalancing_params = self._pack_3( [new_allowed_extra_profit, new_adjustment_step, new_ma_time] ) # Set xcp oracle moving average window time: new_xcp_ma_time: uint256 = _new_xcp_ma_time if new_xcp_ma_time < 872542: assert new_xcp_ma_time > 86 # dev: xcp MA time should be longer than 60/ln(2) else: new_xcp_ma_time = self.xcp_ma_time self.xcp_ma_time = new_xcp_ma_time # ---------------------------------- LOG --------------------------------- log NewParameters( new_mid_fee, new_out_fee, new_fee_gamma, new_allowed_extra_profit, new_adjustment_step, new_ma_time, _new_xcp_ma_time, )
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"name":"Transfer","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"receiver","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"uint256","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"uint256","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false},{"name":"fee","type":"uint256","indexed":false},{"name":"packed_price_scale","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fee","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false},{"name":"packed_price_scale","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_index","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false},{"name":"approx_fee","type":"uint256","indexed":false},{"name":"packed_price_scale","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"NewParameters","inputs":[{"name":"mid_fee","type":"uint256","indexed":false},{"name":"out_fee","type":"uint256","indexed":false},{"name":"fee_gamma","type":"uint256","indexed":false},{"name":"allowed_extra_profit","type":"uint256","indexed":false},{"name":"adjustment_step","type":"uint256","indexed":false},{"name":"ma_time","type":"uint256","indexed":false},{"name":"xcp_ma_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RampAgamma","inputs":[{"name":"initial_A","type":"uint256","indexed":false},{"name":"future_A","type":"uint256","indexed":false},{"name":"initial_gamma","type":"uint256","indexed":false},{"name":"future_gamma","type":"uint256","indexed":false},{"name":"initial_time","type":"uint256","indexed":false},{"name":"future_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StopRampA","inputs":[{"name":"current_A","type":"uint256","indexed":false},{"name":"current_gamma","type":"uint256","indexed":false},{"name":"time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ClaimAdminFee","inputs":[{"name":"admin","type":"address","indexed":true},{"name":"tokens","type":"uint256[2]","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_coins","type":"address[2]"},{"name":"_math","type":"address"},{"name":"_salt","type":"bytes32"},{"name":"packed_precisions","type":"uint256"},{"name":"packed_gamma_A","type":"uint256"},{"name":"packed_fee_params","type":"uint256"},{"name":"packed_rebalancing_params","type":"uint256"},{"name":"initial_price","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_received","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_received","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"transferFrom","inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"transfer","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"approve","inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"permit","inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_deadline","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"fee_receiver","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"admin","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"deposit","type":"bool"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dx","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"lp_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_oracle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"xcp_oracle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_scale","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_withdraw_one_coin","inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_token_fee","inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"xp","type":"uint256[2]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"mid_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"out_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"fee_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"allowed_extra_profit","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"adjustment_step","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"precisions","inputs":[],"outputs":[{"name":"","type":"uint256[2]"}]},{"stateMutability":"view","type":"function","name":"fee_calc","inputs":[{"name":"xp","type":"uint256[2]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"DOMAIN_SEPARATOR","inputs":[],"outputs":[{"name":"","type":"bytes32"}]},{"stateMutability":"nonpayable","type":"function","name":"ramp_A_gamma","inputs":[{"name":"future_A","type":"uint256"},{"name":"future_gamma","type":"uint256"},{"name":"future_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"stop_ramp_A_gamma","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"apply_new_parameters","inputs":[{"name":"_new_mid_fee","type":"uint256"},{"name":"_new_out_fee","type":"uint256"},{"name":"_new_fee_gamma","type":"uint256"},{"name":"_new_allowed_extra_profit","type":"uint256"},{"name":"_new_adjustment_step","type":"uint256"},{"name":"_new_ma_time","type":"uint256"},{"name":"_new_xcp_ma_time","type":"uint256"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"MATH","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"coins","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"factory","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"last_prices","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"last_timestamp","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"last_xcp","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"xcp_ma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A_gamma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A_gamma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"balances","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"D","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"xcp_profit","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"xcp_profit_a","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"packed_rebalancing_params","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"packed_fee_params","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ADMIN_FEE","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"name","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8"}]},{"stateMutability":"view","type":"function","name":"version","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"allowance","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"nonces","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"salt","inputs":[],"outputs":[{"name":"","type":"bytes32"}]}]
Contract Creation Code
614eda5150346103cd576020615103600039600051604060208261510301600039600051116103cd576020602082615103016000396000510180826151030160803950506020615123600039600051602060208261510301600039600051116103cd576020602082615103016000396000510180826151030160e039505060206151436000396000518060a01c6103cd576101205260206151636000396000518060a01c6103cd576101405260206151836000396000518060a01c6103cd576101605261016051614d5a5233614dba52602060805101600081601f0160051c600381116103cd57801561010d57905b8060051b608001518160051b60c001614d1a01526001018181186100ee575b505050602060e05101600081601f0160051c600281116103cd57801561014f57905b8060051b60e001518160051b61012001614d1a015260010181811861012f575b50505061012051614d7a5261014051614d9a5260206151c3604039610175610180610399565b6101808051614d1a526020810151614d3a525060206151e360403961019b6101c0610399565b6101c080516101805260208101516101a052506402540be40061018051106103cd5766b1a2bc2ec5000061018051116103cd57610fa06101a051106103cd576302625a006101a051116103cd5760206151e360003960005160085560206151e3600039600051600a5560206152236000396000516012556020615203600039600051601355602061524360003960005160015560206152436000396000516002556020615243600039600051600455426040524260605261025d6101c06103be565b6101c051600555670de0b6b3a764000060105561f3746007556020614dda5101600081601f0160051c600381116103cd5780156102b657905b8060051b60c001614d1a01518160051b6101c00152600101818118610296575b5050506101c0805160208201209050614e7a5260206151a3600039600051614eba5246614e9a527fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac564726101e052614e7a51610200527fd61c1033330c368dfc371f5b1e7133f4794e104642e5a3c87aba7a6a3441c8ff6102205246610240523061026052614eba516102805260c06101c0526101c0805160208201209050614eda523060007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60006101c05260206101c0a3614d1a6103d261000039614efa610000f35b6fffffffffffffffffffffffffffffffff60405116815260405160801c602082015250565b60605160801b60405117815250565b600080fd60003560e01c60026042820660011b614c9601601e39600051565b63ed6c154681186100385734614c91576020614d5a60403960206040f35b63bb7b8b8081186127a45734614c9157600054600214614c9157600e5460405260015460605261006860c0613d69565b60c051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506018548015614c91578082049050905060e052602060e0f36127a4565b63c661065781186100db57602436103417614c9157602060043560018111614c915760051b606001614d1a0160403960206040f35b6349fe9e7781186127a45734614c915760206012546040526100fd6060612bc1565b6060f36127a4565b63c45a015581186101235734614c91576020614dba60403960206040f35b63767691e7811861023b5760a436103417614c91576084358060a01c614c9157610780525b600054600214614c9157600260005560043560405260443560605233608052600160a0526101776107c06127de565b6107c0516107a052604060046104a0376107a0516104e052606435610500526101a161082061380a565b61082080516107c05260208101516107e052604081015161080052506024356040526107c051606052610780516080526101d96129bb565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c600435610820526107a05161084052602435610860526107c051610880526107e0516108a052610800516108c05260c0610820a260206107c06003600055f35b63b137392981186127a45734614c91576020610258610120612a68565b610120602081019050f36127a4565b63c146bf9481186102835734614c915760045460405260206040f35b630fbcee6e811861047957608436103417614c91576064358060a01c614c91576104e0525b600054600214614c915760026000556102bf614136565b6102ca610540612a68565b6105408051610500526020810151610520525060c0366105403761050051610160526105205161018052604060046101a03742600b54116101e05261031061060061451e565b6106008051610540526020810151610560526040810180516105a05260208101516105c0525060808101516105e052506044356105405110156103b3576008610600527f536c6970706167650000000000000000000000000000000000000000000000006106205261060050610600518061062001601f826000031636823750506308c379a06105c05260206105e052601f19601f6106005101166044016105dcfd5b336040526004356060526103c8610600614052565b61060050610500516080526105205160a0526105a05160c0526105c05160e0526105605161010052600061012052610401610620612d5f565b6106205161060052602435604052610540516060526104e0516080526104256129bb565b337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c604060046106203761054051610660526105e05161068052610600516106a05260a0610620a260206105406003600055f35b63f851a44081186127a45734614c915760206020614dba60003960005163f851a440604052602060406004605c845afa6104b8573d600060003e3d6000fd5b60203d10614c91576040518060a01c614c915760805260809050f36127a4565b634d23bfa081186104f45734614c915760055460405260206040f35b634469ed1481186127a45734614c915764012a05f20060405260206040f36127a4565b63175753e981186105335734614c915760065460405260206040f35b63f9ed959781186127a45734614c9157600b5460405260206040f36127a4565b6399f6bdda81186127a45734614c915760075460405260206040f36127a4565b63204fe3d581186127a45734614c915760085460405260206040f36127a4565b63e89876ff81186127a45734614c915760095460405260206040f36127a4565b63f30cfad581186127a45734614c9157600a5460405260206040f36127a4565b634903b0d1811861060057602436103417614c915760043560018111614c9157600c015460405260206040f35b6323b872dd81186127a457606436103417614c91576004358060a01c614c915760c0526024358060a01c614c915760e052601760c051602052600052604060002080336020526000526040600020905054610100527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010051146106a95760c0516040523360605261010051604435808203828111614c9157905090506080526106a961496e565b60c05160405260e0516060526044356080526106c36149c7565b6001610120526020610120f36127a4565b630f529ba281186106f05734614c9157600e5460405260206040f35b63dd62ed3e811861074c57604436103417614c91576004358060a01c614c91576040526024358060a01c614c91576060526017604051602052600052604060002080606051602052600052604060002090505460805260206080f35b6323c6afea81186127a45734614c9157600054600214614c915760055460405261077660806127aa565b6080602081019050516060526003546080524260605110156108a2576020614d5a6000396000516381d18d8760c0526007546060514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c915760000360e052602060c0602460dc845afa61081b573d600060003e3d6000fd5b60203d10614c915760c090505160a05260065460a05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c91579050905060805160a051808202811583838304141715614c915790509050808201828110614c915790509050670de0b6b3a76400008104905060c052602060c06108a7565b602060805bf36127a4565b637ba1a74d81186108c95734614c9157600f5460405260206040f35b63313ce56781186127a45734614c9157601260405260206040f36127a4565b630b7b594b81186109045734614c915760105460405260206040f35b6370a08231811861094157602436103417614c91576004358060a01c614c9157604052601660405160205260005260406000205460605260206060f35b63b9e8c9fd81186127a45734614c9157600054600214614c915760015460405260206040f36127a4565b630c46b72a81186127a45734614c915760115460405260206040f36127a4565b633dd6547881186127a45734614c915760125460405260206040f36127a4565b63e361640581186127a45734614c915760135460405260206040f36127a4565b6306fdde038118610a245734614c91576020806040528060400160206020614dda6000396000510180614dda8339508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6318160ddd81186127a45734614c915760185460405260206040f36127a4565b6395d89b418118610a9d5734614c91576020806040528060400160206020614e3a6000396000510180614e3a8339508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b63bfa0b1338118610abb5734614c91576020614eba60403960206040f35b63d505accf81186127a45760e436103417614c91576004358060a01c614c9157610120526024358060a01c614c9157610140526084358060081c614c9157610160526101205115614c91576064354211614c915760196101205160205260005260406000205461018052600060026101c0527f19010000000000000000000000000000000000000000000000000000000000006101e0526101c0805160208201836103200181518152505080830192505050610b78610200614a63565b610200518161032001526020810190507f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c961024052610120516102605261014051610280526044356102a052610180516102c0526064356102e05260c061022052610220805160208201209050816103200152602081019050806103005261030090508051602082012090506101a052610120516000610240526101a0516101c052610160516101e052604060a461020037602061024060806101c060015afa506102405118614c9157600161018051016019610120516020526000526040600020556101205160405261014051606052604435608052610c7761496e565b60016101c05260206101c0f36127a4565b6354fd4d5081186127a45734614c915760208060805260066040527f76322e302e30000000000000000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f36127a4565b637ecebe008114600336111615610d4e57602436103417614c91576004358060a01c614c9157604052601960405160205260005260406000205460605260206060f35b634fb08c5e81186127a457604436103417614c91576020610d706104e0612a68565b6104e080516105c05260208101516105e05250604060046106003742600b5411610640526105c051610160526105e05161018052610600516101a052610620516101c052610640516101e052610dc761052061451e565b610520f36127a4565b635b41b9088118610def57608436103417614c91573361078052610e3a565b63f446c1d081186127a45734614c91576020610e0c610120612a68565b610120f36127a4565b63a64833a081186127a45760a436103417614c91576084358060a01c614c9157610780525b600054600214614c9157600260005560043560405260443560605233608052600060a052610e696107c06127de565b6107c0516107a052604060046104a0376107a0516104e05260643561050052610e9361082061380a565b61082080516107c05260208101516107e052604081015161080052506024356040526107c05160605261078051608052610ecb6129bb565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c600435610820526107a05161084052602435610860526107c051610880526107e0516108a052610800516108c05260c0610820a260206107c06003600055f36127a4565b6329b244bb81186127a457608436103417614c91573361078052610148566127a4565b630b4c7e4d8118610f7357606436103417614c9157336104a052610ff7565b63cab4d3db81186127a45734614c915760206020614dba60003960005163cab4d3db604052602060406004605c845afa610fb2573d600060003e3d6000fd5b60203d10614c91576040518060a01c614c915760805260809050f36127a4565b630c3e4b5481186127a457608436103417614c91576064358060a01c614c91576104a0525b600054600214614c91576002600055611011610500612a68565b61050080516104c05260208101516104e05250600c5461050052600d546105205260a03661054037600435602435808201828110614c91579050905015614c91576001546105e052610500516106005261052051610620526040366106403760006002905b80610680526106805160018111614c915760051b600401351561112757610680516040526106805160018111614c915760051b6004013560605233608052600060a0526110c46106a06127de565b6106a0516106805160018111614c915760051b61064001526106805160018111614c915760051b61050001516106805160018111614c915760051b6106400151808201828110614c9157905090506106805160018111614c915760051b61050001525b600101818118611076575050610500516020614d1a600039600051808202811583838304141715614c91579050905061068052670de0b6b3a7640000610520516105e051808202811583838304141715614c9157905090506020614d3a600039600051808202811583838304141715614c915790509050046106a05261068051610500526106a05161052052610600516020614d1a600039600051808202811583838304141715614c91579050905061068052670de0b6b3a7640000610620516105e051808202811583838304141715614c9157905090506020614d3a600039600051808202811583838304141715614c915790509050046106a05261068051610600526106a0516106205260006002905b80610680526106805160018111614c915760051b6106400151156112a2576106805160018111614c915760051b61050001516106805160018111614c915760051b6106000151808203828111614c9157905090506106805160018111614c915760051b61054001525b60010181811861123957505042600b54116112c357600e546105c05261132c565b6020614d5a60003960005163e6864766610680526104c0516106a0526104e0516106c052610600516106e0526106205161070052600061072052602061068060a461069c845afa611319573d600060003e3d6000fd5b60203d10614c91576106809050516105c0525b6020614d5a60003960005163e68647666106a0526104c0516106c0526104e0516106e0526105005161070052610520516107205260006107405260206106a060a46106bc845afa611382573d600060003e3d6000fd5b60203d10614c91576106a0905051610680526018546106a0526105c051156113ec576106a05161068051808202811583838304141715614c9157905090506105c0518015614c9157808204905090506106a051808203828111614c9157905090506105805261140e565b610680516040526105e0516060526114056106c0613d69565b6106c051610580525b6105805115614c91576105c0511561154c5761054051610160526105605161018052610500516101a052610520516101c05261144b6106c0613eb5565b6106c05161058051808202811583838304141715614c9157905090506402540be4008104905060018101818110614c915790506105a052610580516105a051808203828111614c915790509050610580526106a05161058051808201828110614c9157905090506106a0526104a051604052610580516060526114cf6106c0613fdd565b6106c0506015546402540be4006105a05164012a05f20081028164012a05f200820418614c9157905004808201828110614c9157905090506015556104c0516080526104e05160a0526105005160c0526105205160e052610680516101005260006101205261153f6106c0612d5f565b6106c0516105e05261159c565b61068051600e55670de0b6b3a7640000601155670de0b6b3a7640000600f55670de0b6b3a7640000601055610580516003556104a051604052610580516060526115976106c0613fdd565b6106c0505b60443561058051101561160f5760086106c0527f536c6970706167650000000000000000000000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6104a0517f7196cbf63df1f2ec20638e683ebe51d18260be510592ee1e2efe3f3cfd4c33e9610640516106c052610660516106e0526105a051610700526106a051610720526105e0516107405260a06106c0a260206105806003600055f36127a4565b635b36389c811861169157606436103417614c915733610120526116e0565b6392526c0c81186127a45734614c915760206013546040526116b36060612bc1565b6060f36127a4565b633eb1719f8118611ba657608436103417614c91576064358060a01c614c9157610120525b600054600214614c9157600260005560043561014052600c5461016052600d54610180526040366101a0376018546101e05233604052600435606052611727610200614052565b610200506101e051610140511861177c5760006002905b80610200526102005160018111614c915760051b61016001516102005160018111614c915760051b6101a0015260010181811861173e575050611826565b6101405160018103818111614c915790506101405260006002905b80610200526102005160018111614c915760051b610160015161014051808202811583838304141715614c9157905090506101e0518015614c9157808204905090506102005160018111614c915760051b6101a001526102005160018111614c915760051b602401356102005160018111614c915760051b6101a0015110614c91576001018181186117975750505b600e5461020052610200516101e0516102005161014051808202811583838304141715614c91579050905004808203828111614c915790509050600e5560006002905b8061022052610220516040526102205160018111614c915760051b6101a001516060526101205160805261189b6129bb565b600101818118611869575050337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce8001536101a051610220526101c051610240526101e051600435808203828111614c915790509050610260526060610220a2600c54604052600d546060526001546080526119156102606140c7565b610260805161022052602081015161024052506102205161024051808202811583838304141715614c9157905090508060b571010000000000000000000000000000000000821061196d578160801c91508060401b90505b6901000000000000000000821061198b578160401c91508060201b90505b6501000000000082106119a5578160201c91508060101b90505b630100000082106119bd578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808304808281188284100218905090509050905061026052600554604052611a396102c06127aa565b6102c080516102805260208101516102a05250426102a0511015611b93576003546102c0526020614d5a6000396000516381d18d87610300526007546102a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c9157600003610320526020610300602461031c845afa611aec573d600060003e3d6000fd5b60203d10614c91576103009050516102e052670de0b6b3a7640000610260516102e05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c9157905090506102c0516102e051808202811583838304141715614c915790509050808201828110614c91579050905004600355426102a052610280516040526102a051606052611b8b6103006127cf565b610300516005555b6102605160065560406101a06003600055f35b63556d6e9f8118611c4657606436103417614c91576020614dba60003960005163e31593d8606052602060606004607c845afa611be8573d600060003e3d6000fd5b60203d10614c91576060518060a01c614c915760a05260a09050516040526020604051633bb1f8c1606052606060046080373060e052602060606084607c845afa611c38573d600060003e3d6000fd5b60203d10614c915760609050f35b6354f0f7d581186127a45734614c9157600054600214614c91576011548060011b818160011c18614c91579050611c7e610160614af8565b61016051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790508060b5710100000000000000000000000000000000008210611cc9578160801c91508060401b90505b69010000000000000000008210611ce7578160401c91508060201b90505b650100000000008210611d01578160201c91508060101b90505b63010000008210611d19578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050808202811583838304141715614c915790509050670de0b6b3a764000081049050610180526020610180f36127a4565b63f1dc3cc98118611dcf57606436103417614c9157336104e0526102a8565b63083812e581186127a45734614c91576020601254604052611df16060612bc1565b6060602081019050f36127a4565b63a9059cbb81186127a457604436103417614c91576004358060a01c614c915760c0523360405260c051606052602435608052611e3a6149c7565b600160e052602060e0f36127a4565b63095ea7b381186127a457604436103417614c91576004358060a01c614c915760c0523360405260c051606052602435608052611e8461496e565b600160e052602060e0f36127a4565b63ed8e84f38118611f4857606436103417614c91576044358060011c614c91576040526020614dba60003960005163e31593d8608052602060806004609c845afa611ee3573d600060003e3d6000fd5b60203d10614c91576080518060a01c614c915760c05260c0905051606052602060605163bc5bc6b76080526040600460a03760405160e0523061010052602060806084609c845afa611f3a573d600060003e3d6000fd5b60203d10614c915760809050f35b63ddca3f4381186127a45734614c91576020600c54604052600d54606052600154608052611f776101606140c7565b61016080516101c05260208101516101e052506101c0516060526101e051608052611fa36101a0612bfd565b6101a0f36127a4565b6337ed3a7a81186127a457606436103417614c91576020614dba60003960005163e31593d8606052602060606004607c845afa611fee573d600060003e3d6000fd5b60203d10614c91576060518060a01c614c915760a05260a090505160405260206040516399bf0b76606052606060046080373060e052602060606084607c845afa61203e573d600060003e3d6000fd5b60203d10614c915760609050f36127a4565b6386fc88d381186127a45734614c9157600054600214614c91576020612077610160614af8565b610160f36127a4565b63bcc8342e81186120b757608436103417614c915760206040600461016037604060446101a0376120b2610280613eb5565b610280f35b635e24807281186127a457606436103417614c91576020614dba60003960005163f851a440610120526020610120600461013c845afa6120fc573d600060003e3d6000fd5b60203d10614c9157610120518060a01c614c9157610160526101609050513318614c91576009546201517f8101818110614c91579050421115614c915742620151808101818110614c9157905060018103818111614c915790506044351115614c915761216a610160612a68565b610160805161012052602081015161014052506101205160801b6101605261014051610160511761016052610fa060043510614c91576302625a0060043511614c91576402540be40060243510614c915766b1a2bc2ec5000060243511614c9157600435670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610120518015614c91578082049050905061018052678ac7230489e800006101805111614c915767016345785d8a00006101805110614c9157602435670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610140518015614c91578082049050905061018052678ac7230489e800006101805111614c915767016345785d8a00006101805110614c9157610160516008554260095560043560801b6101a0526024356101a051176101a052604435600b556101a051600a557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd05610120516101c0526004356101e05261014051610200526024356102205242610240526044356102605260c06101c0a1006127a4565b63ee8de67581186127a45734614c915760206013546040526123266060612bc1565b6060602081019050f36127a4565b6372d4f0e281186127a45734614c915760206013546040526123566060612bc1565b6060604081019050f36127a4565b6309c3da6a81186127a45734614c91576012546040526123846060612bc1565b6060604081019050516102b68102816102b6820418614c915790506103e88104905060c052602060c0f36127a4565b633620604b81186127a45734614c91576040614d1a60403960406040f36127a4565b6380823d9e811861240357604436103417614c91576020604060046060376123fe610160612bfd565b610160f35b63244c7c2e81186127a45734614c91576020614dba60003960005163f851a440610120526020610120600461013c845afa612443573d600060003e3d6000fd5b60203d10614c9157610120518060a01c614c9157610160526101609050513318614c9157612472610160612a68565b610160805161012052602081015161014052506101205160801b61016052610140516101605117610160526101605160085561016051600a554260095542600b557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc2846101205161018052610140516101a052426101c0526060610180a1006127a4565b633644e51581186127a45734614c91576020612512610120614a63565b610120f36127a4565b6375f6602681186127a45760e436103417614c9157600054600214614c915760026000556020614dba60003960005163f851a44060a052602060a0600460bc845afa61256c573d600060003e3d6000fd5b60203d10614c915760a0518060a01c614c915760e05260e09050513318614c91576060600460a0376013546040526125a5610160612bc1565b610160805161010052602081015161012052604081015161014052506402540be40060c05111156125dc576101205160c0526125e9565b6207a12060c05110614c91575b6402540be40160a051106125ff576101005160a0525b60c05160a05111614c9157670de0b6b3a763ffff60e0511115612628576101405160e052612631565b60e05115614c91575b60a05160405260c05160605260e05160805261264e610160614958565b61016051601355606060646101603760125460405261266e610220612bc1565b61022080516101c05260208101516101e05260408101516102005250670de0b6b3a764000161016051106126a5576101c051610160525b670de0b6b3a764000161018051106126c0576101e051610180525b620d505d6101a05111156126db57610200516101a0526126e7565b60576101a05110614c91575b61016051604052610180516060526101a051608052612707610220614958565b6102205160125560c43561022052620d505d61022051111561272f576007546102205261273b565b60576102205110614c91575b610220516007557f1c65bbdc939f346e5d6f0bde1f072819947438d4fc7b182cc59c2f6dc550408760a0516102405260c0516102605260e05161028052610160516102a052610180516102c0526101a0516102e05260c4356103005260e0610240a16003600055005b60006000fd5b6fffffffffffffffffffffffffffffffff60405116815260405160801c602082015250565b60605160801b60405117815250565b602060405160018111614c915760051b606001614d1a016000396000516370a0823160e0523061010052602060e0602460fc845afa612822573d600060003e3d6000fd5b60203d10614c915760e090505160c05260a051156128975760c05160405160018111614c9157600c0154808203828111614c91579050905060e05260605160e05110614c915760405160018111614c9157600c01805460e051808201828110614c91579050905081555060e0518152506129b9565b602060405160018111614c915760051b606001614d1a016000396000516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc6000855af16128eb573d600060003e3d6000fd5b3d61290257803b15614c915760016101605261291a565b60203d10614c915760e0518060011c614c9157610160525b61016090505115614c9157602060405160018111614c915760051b606001614d1a016000396000516370a082316101005230610120526020610100602461011c845afa61296c573d600060003e3d6000fd5b60203d10614c915761010090505160c051808203828111614c91579050905060e05260405160018111614c9157600c01805460e051808201828110614c91579050905081555060e0518152505b565b60405160018111614c9157600c018054606051808203828111614c915790509050815550602060405160018111614c915760051b606001614d1a0160003960005163a9059cbb60a05260805160c05260605160e052602060a0604460bc6000855af1612a2c573d600060003e3d6000fd5b3d612a4357803b15614c9157600161010052612a5b565b60203d10614c915760a0518060011c614c9157610100525b61010090505115614c9157565b600b54604052600a546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a052604051421015612bb15760085460c05260095460e05260405160e051808203828111614c9157905090506040524260e051808203828111614c91579050905060e05260405160e051808203828111614c9157905090506101005260c05160801c61010051808202811583838304141715614c91579050905060a05160e051808202811583838304141715614c915790509050808201828110614c9157905090506040518015614c91578082049050905060a0526fffffffffffffffffffffffffffffffff60c0511661010051808202811583838304141715614c91579050905060805160e051808202811583838304141715614c915790509050808201828110614c9157905090506040518015614c9157808204905090506080525b60a0518152608051602082015250565b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b601354604052612c0e610100612bc1565b610100805160a052602081015160c052604081015160e05250606051608051808201828110614c9157905090506101005260e051670de0b6b3a7640000810281670de0b6b3a7640000820418614c9157905060e051670de0b6b3a76400008101818110614c91579050606051673782dace9d900000810281673782dace9d900000820418614c91579050610100518015614c915780820490509050608051808202811583838304141715614c915790509050610100518015614c915780820490509050808203828111614c9157905090508015614c91578082049050905061010052670de0b6b3a764000060a05161010051808202811583838304141715614c91579050905060c0516101005180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c915790509050808201828110614c91579050905004815250565b600254610140526004546101605260015461018052601254604052612d85610200612bc1565b61020080516101a05260208101516101c05260408101516101e0525060185461020052600f546102205260115461024052600554604052612dc76102a06127aa565b6102a08051610260526020810151610280525060006102a05242610260511015612f2a576020614d5a6000396000516381d18d876102c0526101e051610260514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c91576000036102e05260206102c060246102dc845afa612e7a573d600060003e3d6000fd5b60203d10614c91576102c09050516102a052670de0b6b3a764000061016051610180518060011b818160011c18614c91579050808281188284100218905090506102a05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c915790509050610140516102a051808202811583838304141715614c915790509050808201828110614c91579050905004610140526101405160025542610260525b42610280511015613050576003546102c0526020614d5a6000396000516381d18d876102e052600754610280514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c91576000036103005260206102e060246102fc845afa612fca573d600060003e3d6000fd5b60203d10614c91576102e09050516102a052670de0b6b3a76400006006546102a05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c9157905090506102c0516102a051808202811583838304141715614c915790509050808201828110614c9157905090500460035542610280525b61026051604052610280516060526130696102c06127cf565b6102c051600555610100516102c052610100516130e7576020614d5a60003960005163e68647666102e0526080516103005260a0516103205260c0516103405260e05161036052610120516103805260206102e060a46102fc845afa6130d4573d600060003e3d6000fd5b60203d10614c91576102e09050516102c0525b670de0b6b3a76400006020614d5a600039600051637e0e395e6102e05260c0516103005260e051610320526102c051610340526080516103605260a0516103805260206102e060a46102fc845afa613144573d600060003e3d6000fd5b60203d10614c91576102e090505161018051808202811583838304141715614c915790509050046004556102c05160011c6102e0526102c051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610180518060011b818160011c18614c915790508015614c91578082049050905061030052670de0b6b3a764000061032052670de0b6b3a76400006103405261024051156133c4576102e05161030051808202811583838304141715614c9157905090508060b5710100000000000000000000000000000000008210613228578160801c91508060401b90505b69010000000000000000008210613246578160401c91508060201b90505b650100000000008210613260578160201c91508060101b90505b63010000008210613278578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090506103605261036051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610200518015614c91578082049050905061034052610240516102205161034051808202811583838304141715614c915790509050046103205242600b5410156133bc576102405161034051116133bc576004610380527f4c6f7373000000000000000000000000000000000000000000000000000000006103a0526103805061038051806103a001601f826000031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b610360516006555b61032051600f55610320516101a0518060011b818160011c18614c91579050808201828110614c915790509050610340518060011b818160011c18614c91579050670de0b6b3a76400008103818111614c9157905011156137f25761018051670de0b6b3a764000061014051020461036052670de0b6b3a76400016103605110156134605761036051670de0b6b3a76400000361036052613473565b670de0b6b3a76400006103605103610360525b6101c051600561036051048082811882841102189050905061038052610380516103605111156137f2576103605161018051610380516103605103808202811583838304141715614c9157905090506103805161014051808202811583838304141715614c915790509050808201828110614c915790509050046103a05260c0516102e0526101805160e0516103a051808202811583838304141715614c91579050905004610300526020614d5a60003960005163e68647666103e0526080516104005260a051610420526102e05161044052610300516104605260006104805260206103e060a46103fc845afa613570573d600060003e3d6000fd5b60203d10614c91576103e09050516103c05260006002905b806103e0526103e05160018111614c915760051b6102e00151670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506103c0518015614c91578082049050905061040052662386f26fc100006104005110156135ed5760006135fe565b68056bc75e2d631000006104005111155b15614c91576001018181186135885750506103c05160011c6102e0526103c051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506103a0518060011b818160011c18614c915790508015614c91578082049050905061030052610200516102e05161030051808202811583838304141715614c9157905090508060b57101000000000000000000000000000000000082106136aa578160801c91508060401b90505b690100000000000000000082106136c8578160401c91508060201b90505b6501000000000082106136e2578160201c91508060101b90505b630100000082106136fa578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790500461024052670de0b6b3a764000161024051101561379e5760006137cc565b61032051610240518060011b818160011c18614c91579050670de0b6b3a76400008103818111614c91579050115b156137f2576103c051600e55610240516011556103a0516001556103a051815250613808565b6102c051600e5561034051601155610180518152505b565b6104c0516104a05114614c91576104e05115614c915761382b610560612a68565b61056080516105205260208101516105405250600c5461056052600d546105805260006105a0526104c05160018111614c915760051b61056001516105c0526104a05160018111614c915760051b61056001516104e051808203828111614c9157905090506105e05260015461060052610560516020614d1a600039600051808202811583838304141715614c91579050905061062052670de0b6b3a76400006105805161060051808202811583838304141715614c9157905090506020614d3a600039600051808202811583838304141715614c915790509050046106405261062051610560526106405161058052600b546106205242610620511115613a48576105e05160206104a05160018111614c915760051b614d1a01600039600051808202811583838304141715614c9157905090506105e0526104a0511561399857670de0b6b3a76400006105e05161060051808202811583838304141715614c915790509050046105e0525b6104a05160018111614c915760051b6105600151610640526105e0516104a05160018111614c915760051b61056001526020614d5a60003960005163e6864766610660526105205161068052610540516106a052610560516106c052610580516106e052600061070052602061066060a461067c845afa613a1e573d600060003e3d6000fd5b60203d10614c9157610660905051600e55610640516104a05160018111614c915760051b61056001525b600e54610640526020614d5a6000396000516343d188fb6106a052610520516106c052610540516106e0526105605161070052610580516107205261064051610740526104c0516107605260406106a060c46106bc845afa613aaf573d600060003e3d6000fd5b60403d10614c91576106a09050805161066052602081015161068052506104c05160018111614c915760051b610560015161066051808203828111614c9157905090506105a0526104c05160018111614c915760051b6105600180516105a051808203828111614c9157905090508152506105a05160018103818111614c915790506105a0526104c05115613b76576105a051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610600518015614c9157808204905090506105a0525b6105a05160206104c05160018111614c915760051b614d1a016000396000518015614c9157808204905090506105a0526402540be4006105605160605261058051608052613bc56106c0612bfd565b6106c0516105a051808202811583838304141715614c915790509050046106a0526105a0516106a051808203828111614c9157905090506105a052610500516105a0511015613c745760086106c0527f536c6970706167650000000000000000000000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6105c0516105a051808203828111614c9157905090506105c0526105c05160206104c05160018111614c915760051b614d1a01600039600051808202811583838304141715614c9157905090506105c0526104c05115613cf957670de0b6b3a76400006105c05161060051808202811583838304141715614c915790509050046105c0525b6105c0516104c05160018111614c915760051b6105600152610520516080526105405160a0526105605160c0526105805160e0526000610100526106805161012052613d466106c0612d5f565b6106c051610600526105a05181526106a051602082015261060051604082015250565b60405160011c608052604051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506060518060011b818160011c18614c915790508015614c91578082049050905060a05260805160a051808202811583838304141715614c9157905090508060b5710100000000000000000000000000000000008210613df9578160801c91508060401b90505b69010000000000000000008210613e17578160401c91508060201b90505b650100000000008210613e31578160201c91508060101b90505b63010000008210613e49578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050815250565b6101a0516060526101c051608052613ece610200612bfd565b6102005160011b60021c6101e05260006102005260006002905b8060051b6101600151610220526102005161022051808201828110614c91579050905061020052600101818118613ee85750506102005160011c6102205260006102405260006002905b8060051b610160015161026052610220516102605111613f705761024051610260516102205103808201828110614c91579050905061024052613f90565b61024051610220516102605103808201828110614c915790509050610240525b600101818118613f325750506101e05161024051808202811583838304141715614c915790509050610200518015614c915780820490509050620186a08101818110614c91579050815250565b601854606051808201828110614c915790509050601855601660405160205260005260406000208054606051808201828110614c91579050905081555060405160007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601854606051808203828111614c915790509050601855601660405160205260005260406000208054606051808203828111614c91579050905081555060006040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b6040516020614d1a600039600051808202811583838304141715614c9157905090508152670de0b6b3a76400006060516020614d3a600039600051808202811583838304141715614c915790509050608051808202811583838304141715614c91579050905004602082015250565b601454610120526201517f61012051420311156141575742600b541161415a565b60015b156141645761451c565b600f546101405260105461016052601854610180526101605161014051111561419b57670de0b6b3a763ffff61018051111561419e565b60015b156141a85761451c565b6141b36101e0612a68565b6101e080516101a05260208101516101c05250600e546101e05260115461020052600154610220526020614dba60003960005163cab4d3db610260526020610260600461027c845afa61420b573d600060003e3d6000fd5b60203d10614c9157610260518060a01c614c91576102a0526102a090505161024052600c5461026052600d54610280526404a817c80061016051610140510364012a05f20081028164012a05f200820418614c91579050046102a0526015546102c05260006102e0526102405115614288576102a051151561428b565b60005b156143575761020051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610200516102a051808203828111614c9157905090508015614c915780820490509050670de0b6b3a76400008103818111614c915790506102e0526102c051610180516102e051808202811583838304141715614c915790509050670de0b6b3a764000081049050808201828110614c9157905090506102c052610140516102a0518060011b818160011c18614c91579050808203828111614c915790509050610140525b610180516102c051808201828110614c915790509050610300526101e0516040526102205160605261438a610320613d69565b61032051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610300518015614c91578082049050905061020052670de0b6b3a763ffff61020051116143d85761451c565b600060155561014051600f5542601455610200516011556101e051610300516101e0516102c051808202811583838304141715614c91579050905004808203828111614c915790509050600e556101605161014051111561443b57610140516010555b604036610320376102c0511561451c5760006002905b80610360526103605160018111614c915760051b61026001516102c051808202811583838304141715614c915790509050610300518015614c9157808204905090506103605160018111614c915760051b6103200152610360516040526103605160018111614c915760051b6103200151606052610240516080526144d46129bb565b600101818118614451575050610240517f3bbd5f2f4711532d6e9ee88dfdf2f1468e9a4c3ae5e14d2e1a67bf4242d008d0610320516103605261034051610380526040610360a25b565b60185461020052610200516101a05111614c915760016101c05111614c9157600c5461022052600d54610240526000610260526001546020614d3a600039600051808202811583838304141715614c91579050905061028052610220516020614d1a600039600051808202811583838304141715614c9157905090506102a052670de0b6b3a76400006102405161028051808202811583838304141715614c915790509050046102c0526101c0516145fe576020614d1a600039600051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610280525b6101e05161461257600e546102605261467b565b6020614d5a60003960005163e68647666102e052610160516103005261018051610320526102a051610340526102c0516103605260006103805260206102e060a46102fc845afa614668573d600060003e3d6000fd5b60203d10614c91576102e0905051610260525b610260516102e0526102a051610300526102c051610320526101c05160018111614c915760051b6102a001518060011b818160011c18614c915790506101a051808202811583838304141715614c915790509050610200518015614c915780820490509050610340526013546040526146f5610380612bc1565b61038060208101905051610360526101c05160018111614c915760051b610300015161034051101561476d576101c05160018111614c915760051b61030001805161034051808203828111614c9157905090508152506103005160605261032051608052614764610380612bfd565b61038051610360525b610200516101a0516102e051808202811583838304141715614c91579050905004610380526103605161038051808202811583838304141715614c9157905090506404a817c8008104905060018101818110614c915790506103a0526103a0518060011b818160011c18614c915790506101c05160018111614c915760051b6102200151808202811583838304141715614c9157905090506102e0518015614c9157808204905090506103c0526102e051610380516103a051808203828111614c915790509050808203828111614c9157905090506102e0526020614d5a6000396000516343d188fb61040052610160516104205261018051610440526102a051610460526102c051610480526102e0516104a0526101c0516104c052604061040060c461041c845afa6148a6573d600060003e3d6000fd5b60403d10614c91576104009050516103e0526101c05160018111614c915760051b6102a001516103e051808203828111614c915790509050670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610280518015614c915780820490509050610400526103e0516101c05160018111614c915760051b6102a001526104005181526102e0516020820152604081016102a05181526102c0516020820152506103c051608082015250565b60805160605160401b60405160801b1717815250565b608051601760405160205260005260406000208060605160205260005260406000209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b6060513081146149d9578015156149dc565b60005b905015614c9157601660405160205260005260406000208054608051808203828111614c915790509050815550601660605160205260005260406000208054608051808201828110614c9157905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b6020614e9a6000396000514614614aed577fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac564726060526020614e7a6080397fd61c1033330c368dfc371f5b1e7133f4794e104642e5a3c87aba7a6a3441c8ff60a0524660c0523060e0526020614eba6101003960c06040526040805160208201209050815250614af6565b6020614eda8239505b565b600254606052600154608052600554604052614b1460c06127aa565b60c05160a0524260a0511015614c885760045460c052601254604052614b3b610100612bc1565b6101006040810190505160e0526020614d5a6000396000516381d18d876101205260a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c9157905060e0518015614c9157808204905090508060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c9157600003610140526020610120602461013c845afa614be1573d600060003e3d6000fd5b60203d10614c91576101209050516101005260c0516080518060011b818160011c18614c91579050808281188284100218905090506101005180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c91579050905060605161010051808202811583838304141715614c915790509050808201828110614c915790509050670de0b6b3a764000081049050815250614c8f565b6060518152505b565b600080fd167227a4055309ab27a4057323d527a427a40f3127a427a427a405930fd227a4096b0f540d0b08e80e15230427a427a427a416bb27a4051727a405d3236405b327a427a427a41db0233420500c8824f527a423b327a400a627a427a4001a27a427a41dff02670a440dd01e4906d41e93251b08ad04d827a4208009cb098b01051fac27a484194d1a8118841901e0a16576797065728300030a0017000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b5204b0be36771253cc38e88012e02b752f0f360000000000000000000000002005995a71243be9fb995dab4742327dc76564dfa0b5d5a7a022a347f3a45ddc070e84b06ef9ba037d5a354978c1b1b9a082df91000000000000000000000000000000010000000000000000000000000000000100000000000000000000000000061a800000000000000000000083e0717e1000000000000000000000000000018cba800000000002aea5400000d12f0c4c60000000000000000000000001d1a94a2000000084c94623200000000000000003620000000000000000000000000000000000000000000000000000736a356c080000000000000000000000000000000000000000000000000000000000000000085a554e2f5745544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075a554e5745544800000000000000000000000000000000000000000000000000
Deployed Bytecode
0x60003560e01c60026042820660011b614c9601601e39600051565b63ed6c154681186100385734614c91576020614d5a60403960206040f35b63bb7b8b8081186127a45734614c9157600054600214614c9157600e5460405260015460605261006860c0613d69565b60c051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506018548015614c91578082049050905060e052602060e0f36127a4565b63c661065781186100db57602436103417614c9157602060043560018111614c915760051b606001614d1a0160403960206040f35b6349fe9e7781186127a45734614c915760206012546040526100fd6060612bc1565b6060f36127a4565b63c45a015581186101235734614c91576020614dba60403960206040f35b63767691e7811861023b5760a436103417614c91576084358060a01c614c9157610780525b600054600214614c9157600260005560043560405260443560605233608052600160a0526101776107c06127de565b6107c0516107a052604060046104a0376107a0516104e052606435610500526101a161082061380a565b61082080516107c05260208101516107e052604081015161080052506024356040526107c051606052610780516080526101d96129bb565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c600435610820526107a05161084052602435610860526107c051610880526107e0516108a052610800516108c05260c0610820a260206107c06003600055f35b63b137392981186127a45734614c91576020610258610120612a68565b610120602081019050f36127a4565b63c146bf9481186102835734614c915760045460405260206040f35b630fbcee6e811861047957608436103417614c91576064358060a01c614c91576104e0525b600054600214614c915760026000556102bf614136565b6102ca610540612a68565b6105408051610500526020810151610520525060c0366105403761050051610160526105205161018052604060046101a03742600b54116101e05261031061060061451e565b6106008051610540526020810151610560526040810180516105a05260208101516105c0525060808101516105e052506044356105405110156103b3576008610600527f536c6970706167650000000000000000000000000000000000000000000000006106205261060050610600518061062001601f826000031636823750506308c379a06105c05260206105e052601f19601f6106005101166044016105dcfd5b336040526004356060526103c8610600614052565b61060050610500516080526105205160a0526105a05160c0526105c05160e0526105605161010052600061012052610401610620612d5f565b6106205161060052602435604052610540516060526104e0516080526104256129bb565b337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c604060046106203761054051610660526105e05161068052610600516106a05260a0610620a260206105406003600055f35b63f851a44081186127a45734614c915760206020614dba60003960005163f851a440604052602060406004605c845afa6104b8573d600060003e3d6000fd5b60203d10614c91576040518060a01c614c915760805260809050f36127a4565b634d23bfa081186104f45734614c915760055460405260206040f35b634469ed1481186127a45734614c915764012a05f20060405260206040f36127a4565b63175753e981186105335734614c915760065460405260206040f35b63f9ed959781186127a45734614c9157600b5460405260206040f36127a4565b6399f6bdda81186127a45734614c915760075460405260206040f36127a4565b63204fe3d581186127a45734614c915760085460405260206040f36127a4565b63e89876ff81186127a45734614c915760095460405260206040f36127a4565b63f30cfad581186127a45734614c9157600a5460405260206040f36127a4565b634903b0d1811861060057602436103417614c915760043560018111614c9157600c015460405260206040f35b6323b872dd81186127a457606436103417614c91576004358060a01c614c915760c0526024358060a01c614c915760e052601760c051602052600052604060002080336020526000526040600020905054610100527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010051146106a95760c0516040523360605261010051604435808203828111614c9157905090506080526106a961496e565b60c05160405260e0516060526044356080526106c36149c7565b6001610120526020610120f36127a4565b630f529ba281186106f05734614c9157600e5460405260206040f35b63dd62ed3e811861074c57604436103417614c91576004358060a01c614c91576040526024358060a01c614c91576060526017604051602052600052604060002080606051602052600052604060002090505460805260206080f35b6323c6afea81186127a45734614c9157600054600214614c915760055460405261077660806127aa565b6080602081019050516060526003546080524260605110156108a2576020614d5a6000396000516381d18d8760c0526007546060514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c915760000360e052602060c0602460dc845afa61081b573d600060003e3d6000fd5b60203d10614c915760c090505160a05260065460a05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c91579050905060805160a051808202811583838304141715614c915790509050808201828110614c915790509050670de0b6b3a76400008104905060c052602060c06108a7565b602060805bf36127a4565b637ba1a74d81186108c95734614c9157600f5460405260206040f35b63313ce56781186127a45734614c9157601260405260206040f36127a4565b630b7b594b81186109045734614c915760105460405260206040f35b6370a08231811861094157602436103417614c91576004358060a01c614c9157604052601660405160205260005260406000205460605260206060f35b63b9e8c9fd81186127a45734614c9157600054600214614c915760015460405260206040f36127a4565b630c46b72a81186127a45734614c915760115460405260206040f36127a4565b633dd6547881186127a45734614c915760125460405260206040f36127a4565b63e361640581186127a45734614c915760135460405260206040f36127a4565b6306fdde038118610a245734614c91576020806040528060400160206020614dda6000396000510180614dda8339508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6318160ddd81186127a45734614c915760185460405260206040f36127a4565b6395d89b418118610a9d5734614c91576020806040528060400160206020614e3a6000396000510180614e3a8339508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b63bfa0b1338118610abb5734614c91576020614eba60403960206040f35b63d505accf81186127a45760e436103417614c91576004358060a01c614c9157610120526024358060a01c614c9157610140526084358060081c614c9157610160526101205115614c91576064354211614c915760196101205160205260005260406000205461018052600060026101c0527f19010000000000000000000000000000000000000000000000000000000000006101e0526101c0805160208201836103200181518152505080830192505050610b78610200614a63565b610200518161032001526020810190507f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c961024052610120516102605261014051610280526044356102a052610180516102c0526064356102e05260c061022052610220805160208201209050816103200152602081019050806103005261030090508051602082012090506101a052610120516000610240526101a0516101c052610160516101e052604060a461020037602061024060806101c060015afa506102405118614c9157600161018051016019610120516020526000526040600020556101205160405261014051606052604435608052610c7761496e565b60016101c05260206101c0f36127a4565b6354fd4d5081186127a45734614c915760208060805260066040527f76322e302e30000000000000000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f36127a4565b637ecebe008114600336111615610d4e57602436103417614c91576004358060a01c614c9157604052601960405160205260005260406000205460605260206060f35b634fb08c5e81186127a457604436103417614c91576020610d706104e0612a68565b6104e080516105c05260208101516105e05250604060046106003742600b5411610640526105c051610160526105e05161018052610600516101a052610620516101c052610640516101e052610dc761052061451e565b610520f36127a4565b635b41b9088118610def57608436103417614c91573361078052610e3a565b63f446c1d081186127a45734614c91576020610e0c610120612a68565b610120f36127a4565b63a64833a081186127a45760a436103417614c91576084358060a01c614c9157610780525b600054600214614c9157600260005560043560405260443560605233608052600060a052610e696107c06127de565b6107c0516107a052604060046104a0376107a0516104e05260643561050052610e9361082061380a565b61082080516107c05260208101516107e052604081015161080052506024356040526107c05160605261078051608052610ecb6129bb565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c600435610820526107a05161084052602435610860526107c051610880526107e0516108a052610800516108c05260c0610820a260206107c06003600055f36127a4565b6329b244bb81186127a457608436103417614c91573361078052610148566127a4565b630b4c7e4d8118610f7357606436103417614c9157336104a052610ff7565b63cab4d3db81186127a45734614c915760206020614dba60003960005163cab4d3db604052602060406004605c845afa610fb2573d600060003e3d6000fd5b60203d10614c91576040518060a01c614c915760805260809050f36127a4565b630c3e4b5481186127a457608436103417614c91576064358060a01c614c91576104a0525b600054600214614c91576002600055611011610500612a68565b61050080516104c05260208101516104e05250600c5461050052600d546105205260a03661054037600435602435808201828110614c91579050905015614c91576001546105e052610500516106005261052051610620526040366106403760006002905b80610680526106805160018111614c915760051b600401351561112757610680516040526106805160018111614c915760051b6004013560605233608052600060a0526110c46106a06127de565b6106a0516106805160018111614c915760051b61064001526106805160018111614c915760051b61050001516106805160018111614c915760051b6106400151808201828110614c9157905090506106805160018111614c915760051b61050001525b600101818118611076575050610500516020614d1a600039600051808202811583838304141715614c91579050905061068052670de0b6b3a7640000610520516105e051808202811583838304141715614c9157905090506020614d3a600039600051808202811583838304141715614c915790509050046106a05261068051610500526106a05161052052610600516020614d1a600039600051808202811583838304141715614c91579050905061068052670de0b6b3a7640000610620516105e051808202811583838304141715614c9157905090506020614d3a600039600051808202811583838304141715614c915790509050046106a05261068051610600526106a0516106205260006002905b80610680526106805160018111614c915760051b6106400151156112a2576106805160018111614c915760051b61050001516106805160018111614c915760051b6106000151808203828111614c9157905090506106805160018111614c915760051b61054001525b60010181811861123957505042600b54116112c357600e546105c05261132c565b6020614d5a60003960005163e6864766610680526104c0516106a0526104e0516106c052610600516106e0526106205161070052600061072052602061068060a461069c845afa611319573d600060003e3d6000fd5b60203d10614c91576106809050516105c0525b6020614d5a60003960005163e68647666106a0526104c0516106c0526104e0516106e0526105005161070052610520516107205260006107405260206106a060a46106bc845afa611382573d600060003e3d6000fd5b60203d10614c91576106a0905051610680526018546106a0526105c051156113ec576106a05161068051808202811583838304141715614c9157905090506105c0518015614c9157808204905090506106a051808203828111614c9157905090506105805261140e565b610680516040526105e0516060526114056106c0613d69565b6106c051610580525b6105805115614c91576105c0511561154c5761054051610160526105605161018052610500516101a052610520516101c05261144b6106c0613eb5565b6106c05161058051808202811583838304141715614c9157905090506402540be4008104905060018101818110614c915790506105a052610580516105a051808203828111614c915790509050610580526106a05161058051808201828110614c9157905090506106a0526104a051604052610580516060526114cf6106c0613fdd565b6106c0506015546402540be4006105a05164012a05f20081028164012a05f200820418614c9157905004808201828110614c9157905090506015556104c0516080526104e05160a0526105005160c0526105205160e052610680516101005260006101205261153f6106c0612d5f565b6106c0516105e05261159c565b61068051600e55670de0b6b3a7640000601155670de0b6b3a7640000600f55670de0b6b3a7640000601055610580516003556104a051604052610580516060526115976106c0613fdd565b6106c0505b60443561058051101561160f5760086106c0527f536c6970706167650000000000000000000000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6104a0517f7196cbf63df1f2ec20638e683ebe51d18260be510592ee1e2efe3f3cfd4c33e9610640516106c052610660516106e0526105a051610700526106a051610720526105e0516107405260a06106c0a260206105806003600055f36127a4565b635b36389c811861169157606436103417614c915733610120526116e0565b6392526c0c81186127a45734614c915760206013546040526116b36060612bc1565b6060f36127a4565b633eb1719f8118611ba657608436103417614c91576064358060a01c614c9157610120525b600054600214614c9157600260005560043561014052600c5461016052600d54610180526040366101a0376018546101e05233604052600435606052611727610200614052565b610200506101e051610140511861177c5760006002905b80610200526102005160018111614c915760051b61016001516102005160018111614c915760051b6101a0015260010181811861173e575050611826565b6101405160018103818111614c915790506101405260006002905b80610200526102005160018111614c915760051b610160015161014051808202811583838304141715614c9157905090506101e0518015614c9157808204905090506102005160018111614c915760051b6101a001526102005160018111614c915760051b602401356102005160018111614c915760051b6101a0015110614c91576001018181186117975750505b600e5461020052610200516101e0516102005161014051808202811583838304141715614c91579050905004808203828111614c915790509050600e5560006002905b8061022052610220516040526102205160018111614c915760051b6101a001516060526101205160805261189b6129bb565b600101818118611869575050337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce8001536101a051610220526101c051610240526101e051600435808203828111614c915790509050610260526060610220a2600c54604052600d546060526001546080526119156102606140c7565b610260805161022052602081015161024052506102205161024051808202811583838304141715614c9157905090508060b571010000000000000000000000000000000000821061196d578160801c91508060401b90505b6901000000000000000000821061198b578160401c91508060201b90505b6501000000000082106119a5578160201c91508060101b90505b630100000082106119bd578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808304808281188284100218905090509050905061026052600554604052611a396102c06127aa565b6102c080516102805260208101516102a05250426102a0511015611b93576003546102c0526020614d5a6000396000516381d18d87610300526007546102a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c9157600003610320526020610300602461031c845afa611aec573d600060003e3d6000fd5b60203d10614c91576103009050516102e052670de0b6b3a7640000610260516102e05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c9157905090506102c0516102e051808202811583838304141715614c915790509050808201828110614c91579050905004600355426102a052610280516040526102a051606052611b8b6103006127cf565b610300516005555b6102605160065560406101a06003600055f35b63556d6e9f8118611c4657606436103417614c91576020614dba60003960005163e31593d8606052602060606004607c845afa611be8573d600060003e3d6000fd5b60203d10614c91576060518060a01c614c915760a05260a09050516040526020604051633bb1f8c1606052606060046080373060e052602060606084607c845afa611c38573d600060003e3d6000fd5b60203d10614c915760609050f35b6354f0f7d581186127a45734614c9157600054600214614c91576011548060011b818160011c18614c91579050611c7e610160614af8565b61016051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790508060b5710100000000000000000000000000000000008210611cc9578160801c91508060401b90505b69010000000000000000008210611ce7578160401c91508060201b90505b650100000000008210611d01578160201c91508060101b90505b63010000008210611d19578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050808202811583838304141715614c915790509050670de0b6b3a764000081049050610180526020610180f36127a4565b63f1dc3cc98118611dcf57606436103417614c9157336104e0526102a8565b63083812e581186127a45734614c91576020601254604052611df16060612bc1565b6060602081019050f36127a4565b63a9059cbb81186127a457604436103417614c91576004358060a01c614c915760c0523360405260c051606052602435608052611e3a6149c7565b600160e052602060e0f36127a4565b63095ea7b381186127a457604436103417614c91576004358060a01c614c915760c0523360405260c051606052602435608052611e8461496e565b600160e052602060e0f36127a4565b63ed8e84f38118611f4857606436103417614c91576044358060011c614c91576040526020614dba60003960005163e31593d8608052602060806004609c845afa611ee3573d600060003e3d6000fd5b60203d10614c91576080518060a01c614c915760c05260c0905051606052602060605163bc5bc6b76080526040600460a03760405160e0523061010052602060806084609c845afa611f3a573d600060003e3d6000fd5b60203d10614c915760809050f35b63ddca3f4381186127a45734614c91576020600c54604052600d54606052600154608052611f776101606140c7565b61016080516101c05260208101516101e052506101c0516060526101e051608052611fa36101a0612bfd565b6101a0f36127a4565b6337ed3a7a81186127a457606436103417614c91576020614dba60003960005163e31593d8606052602060606004607c845afa611fee573d600060003e3d6000fd5b60203d10614c91576060518060a01c614c915760a05260a090505160405260206040516399bf0b76606052606060046080373060e052602060606084607c845afa61203e573d600060003e3d6000fd5b60203d10614c915760609050f36127a4565b6386fc88d381186127a45734614c9157600054600214614c91576020612077610160614af8565b610160f36127a4565b63bcc8342e81186120b757608436103417614c915760206040600461016037604060446101a0376120b2610280613eb5565b610280f35b635e24807281186127a457606436103417614c91576020614dba60003960005163f851a440610120526020610120600461013c845afa6120fc573d600060003e3d6000fd5b60203d10614c9157610120518060a01c614c9157610160526101609050513318614c91576009546201517f8101818110614c91579050421115614c915742620151808101818110614c9157905060018103818111614c915790506044351115614c915761216a610160612a68565b610160805161012052602081015161014052506101205160801b6101605261014051610160511761016052610fa060043510614c91576302625a0060043511614c91576402540be40060243510614c915766b1a2bc2ec5000060243511614c9157600435670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610120518015614c91578082049050905061018052678ac7230489e800006101805111614c915767016345785d8a00006101805110614c9157602435670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610140518015614c91578082049050905061018052678ac7230489e800006101805111614c915767016345785d8a00006101805110614c9157610160516008554260095560043560801b6101a0526024356101a051176101a052604435600b556101a051600a557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd05610120516101c0526004356101e05261014051610200526024356102205242610240526044356102605260c06101c0a1006127a4565b63ee8de67581186127a45734614c915760206013546040526123266060612bc1565b6060602081019050f36127a4565b6372d4f0e281186127a45734614c915760206013546040526123566060612bc1565b6060604081019050f36127a4565b6309c3da6a81186127a45734614c91576012546040526123846060612bc1565b6060604081019050516102b68102816102b6820418614c915790506103e88104905060c052602060c0f36127a4565b633620604b81186127a45734614c91576040614d1a60403960406040f36127a4565b6380823d9e811861240357604436103417614c91576020604060046060376123fe610160612bfd565b610160f35b63244c7c2e81186127a45734614c91576020614dba60003960005163f851a440610120526020610120600461013c845afa612443573d600060003e3d6000fd5b60203d10614c9157610120518060a01c614c9157610160526101609050513318614c9157612472610160612a68565b610160805161012052602081015161014052506101205160801b61016052610140516101605117610160526101605160085561016051600a554260095542600b557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc2846101205161018052610140516101a052426101c0526060610180a1006127a4565b633644e51581186127a45734614c91576020612512610120614a63565b610120f36127a4565b6375f6602681186127a45760e436103417614c9157600054600214614c915760026000556020614dba60003960005163f851a44060a052602060a0600460bc845afa61256c573d600060003e3d6000fd5b60203d10614c915760a0518060a01c614c915760e05260e09050513318614c91576060600460a0376013546040526125a5610160612bc1565b610160805161010052602081015161012052604081015161014052506402540be40060c05111156125dc576101205160c0526125e9565b6207a12060c05110614c91575b6402540be40160a051106125ff576101005160a0525b60c05160a05111614c9157670de0b6b3a763ffff60e0511115612628576101405160e052612631565b60e05115614c91575b60a05160405260c05160605260e05160805261264e610160614958565b61016051601355606060646101603760125460405261266e610220612bc1565b61022080516101c05260208101516101e05260408101516102005250670de0b6b3a764000161016051106126a5576101c051610160525b670de0b6b3a764000161018051106126c0576101e051610180525b620d505d6101a05111156126db57610200516101a0526126e7565b60576101a05110614c91575b61016051604052610180516060526101a051608052612707610220614958565b6102205160125560c43561022052620d505d61022051111561272f576007546102205261273b565b60576102205110614c91575b610220516007557f1c65bbdc939f346e5d6f0bde1f072819947438d4fc7b182cc59c2f6dc550408760a0516102405260c0516102605260e05161028052610160516102a052610180516102c0526101a0516102e05260c4356103005260e0610240a16003600055005b60006000fd5b6fffffffffffffffffffffffffffffffff60405116815260405160801c602082015250565b60605160801b60405117815250565b602060405160018111614c915760051b606001614d1a016000396000516370a0823160e0523061010052602060e0602460fc845afa612822573d600060003e3d6000fd5b60203d10614c915760e090505160c05260a051156128975760c05160405160018111614c9157600c0154808203828111614c91579050905060e05260605160e05110614c915760405160018111614c9157600c01805460e051808201828110614c91579050905081555060e0518152506129b9565b602060405160018111614c915760051b606001614d1a016000396000516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc6000855af16128eb573d600060003e3d6000fd5b3d61290257803b15614c915760016101605261291a565b60203d10614c915760e0518060011c614c9157610160525b61016090505115614c9157602060405160018111614c915760051b606001614d1a016000396000516370a082316101005230610120526020610100602461011c845afa61296c573d600060003e3d6000fd5b60203d10614c915761010090505160c051808203828111614c91579050905060e05260405160018111614c9157600c01805460e051808201828110614c91579050905081555060e0518152505b565b60405160018111614c9157600c018054606051808203828111614c915790509050815550602060405160018111614c915760051b606001614d1a0160003960005163a9059cbb60a05260805160c05260605160e052602060a0604460bc6000855af1612a2c573d600060003e3d6000fd5b3d612a4357803b15614c9157600161010052612a5b565b60203d10614c915760a0518060011c614c9157610100525b61010090505115614c9157565b600b54604052600a546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a052604051421015612bb15760085460c05260095460e05260405160e051808203828111614c9157905090506040524260e051808203828111614c91579050905060e05260405160e051808203828111614c9157905090506101005260c05160801c61010051808202811583838304141715614c91579050905060a05160e051808202811583838304141715614c915790509050808201828110614c9157905090506040518015614c91578082049050905060a0526fffffffffffffffffffffffffffffffff60c0511661010051808202811583838304141715614c91579050905060805160e051808202811583838304141715614c915790509050808201828110614c9157905090506040518015614c9157808204905090506080525b60a0518152608051602082015250565b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b601354604052612c0e610100612bc1565b610100805160a052602081015160c052604081015160e05250606051608051808201828110614c9157905090506101005260e051670de0b6b3a7640000810281670de0b6b3a7640000820418614c9157905060e051670de0b6b3a76400008101818110614c91579050606051673782dace9d900000810281673782dace9d900000820418614c91579050610100518015614c915780820490509050608051808202811583838304141715614c915790509050610100518015614c915780820490509050808203828111614c9157905090508015614c91578082049050905061010052670de0b6b3a764000060a05161010051808202811583838304141715614c91579050905060c0516101005180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c915790509050808201828110614c91579050905004815250565b600254610140526004546101605260015461018052601254604052612d85610200612bc1565b61020080516101a05260208101516101c05260408101516101e0525060185461020052600f546102205260115461024052600554604052612dc76102a06127aa565b6102a08051610260526020810151610280525060006102a05242610260511015612f2a576020614d5a6000396000516381d18d876102c0526101e051610260514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c91576000036102e05260206102c060246102dc845afa612e7a573d600060003e3d6000fd5b60203d10614c91576102c09050516102a052670de0b6b3a764000061016051610180518060011b818160011c18614c91579050808281188284100218905090506102a05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c915790509050610140516102a051808202811583838304141715614c915790509050808201828110614c91579050905004610140526101405160025542610260525b42610280511015613050576003546102c0526020614d5a6000396000516381d18d876102e052600754610280514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050048060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c91576000036103005260206102e060246102fc845afa612fca573d600060003e3d6000fd5b60203d10614c91576102e09050516102a052670de0b6b3a76400006006546102a05180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c9157905090506102c0516102a051808202811583838304141715614c915790509050808201828110614c9157905090500460035542610280525b61026051604052610280516060526130696102c06127cf565b6102c051600555610100516102c052610100516130e7576020614d5a60003960005163e68647666102e0526080516103005260a0516103205260c0516103405260e05161036052610120516103805260206102e060a46102fc845afa6130d4573d600060003e3d6000fd5b60203d10614c91576102e09050516102c0525b670de0b6b3a76400006020614d5a600039600051637e0e395e6102e05260c0516103005260e051610320526102c051610340526080516103605260a0516103805260206102e060a46102fc845afa613144573d600060003e3d6000fd5b60203d10614c91576102e090505161018051808202811583838304141715614c915790509050046004556102c05160011c6102e0526102c051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610180518060011b818160011c18614c915790508015614c91578082049050905061030052670de0b6b3a764000061032052670de0b6b3a76400006103405261024051156133c4576102e05161030051808202811583838304141715614c9157905090508060b5710100000000000000000000000000000000008210613228578160801c91508060401b90505b69010000000000000000008210613246578160401c91508060201b90505b650100000000008210613260578160201c91508060101b90505b63010000008210613278578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090506103605261036051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610200518015614c91578082049050905061034052610240516102205161034051808202811583838304141715614c915790509050046103205242600b5410156133bc576102405161034051116133bc576004610380527f4c6f7373000000000000000000000000000000000000000000000000000000006103a0526103805061038051806103a001601f826000031636823750506308c379a061034052602061036052601f19601f61038051011660440161035cfd5b610360516006555b61032051600f55610320516101a0518060011b818160011c18614c91579050808201828110614c915790509050610340518060011b818160011c18614c91579050670de0b6b3a76400008103818111614c9157905011156137f25761018051670de0b6b3a764000061014051020461036052670de0b6b3a76400016103605110156134605761036051670de0b6b3a76400000361036052613473565b670de0b6b3a76400006103605103610360525b6101c051600561036051048082811882841102189050905061038052610380516103605111156137f2576103605161018051610380516103605103808202811583838304141715614c9157905090506103805161014051808202811583838304141715614c915790509050808201828110614c915790509050046103a05260c0516102e0526101805160e0516103a051808202811583838304141715614c91579050905004610300526020614d5a60003960005163e68647666103e0526080516104005260a051610420526102e05161044052610300516104605260006104805260206103e060a46103fc845afa613570573d600060003e3d6000fd5b60203d10614c91576103e09050516103c05260006002905b806103e0526103e05160018111614c915760051b6102e00151670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506103c0518015614c91578082049050905061040052662386f26fc100006104005110156135ed5760006135fe565b68056bc75e2d631000006104005111155b15614c91576001018181186135885750506103c05160011c6102e0526103c051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506103a0518060011b818160011c18614c915790508015614c91578082049050905061030052610200516102e05161030051808202811583838304141715614c9157905090508060b57101000000000000000000000000000000000082106136aa578160801c91508060401b90505b690100000000000000000082106136c8578160401c91508060201b90505b6501000000000082106136e2578160201c91508060101b90505b630100000082106136fa578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790500461024052670de0b6b3a764000161024051101561379e5760006137cc565b61032051610240518060011b818160011c18614c91579050670de0b6b3a76400008103818111614c91579050115b156137f2576103c051600e55610240516011556103a0516001556103a051815250613808565b6102c051600e5561034051601155610180518152505b565b6104c0516104a05114614c91576104e05115614c915761382b610560612a68565b61056080516105205260208101516105405250600c5461056052600d546105805260006105a0526104c05160018111614c915760051b61056001516105c0526104a05160018111614c915760051b61056001516104e051808203828111614c9157905090506105e05260015461060052610560516020614d1a600039600051808202811583838304141715614c91579050905061062052670de0b6b3a76400006105805161060051808202811583838304141715614c9157905090506020614d3a600039600051808202811583838304141715614c915790509050046106405261062051610560526106405161058052600b546106205242610620511115613a48576105e05160206104a05160018111614c915760051b614d1a01600039600051808202811583838304141715614c9157905090506105e0526104a0511561399857670de0b6b3a76400006105e05161060051808202811583838304141715614c915790509050046105e0525b6104a05160018111614c915760051b6105600151610640526105e0516104a05160018111614c915760051b61056001526020614d5a60003960005163e6864766610660526105205161068052610540516106a052610560516106c052610580516106e052600061070052602061066060a461067c845afa613a1e573d600060003e3d6000fd5b60203d10614c9157610660905051600e55610640516104a05160018111614c915760051b61056001525b600e54610640526020614d5a6000396000516343d188fb6106a052610520516106c052610540516106e0526105605161070052610580516107205261064051610740526104c0516107605260406106a060c46106bc845afa613aaf573d600060003e3d6000fd5b60403d10614c91576106a09050805161066052602081015161068052506104c05160018111614c915760051b610560015161066051808203828111614c9157905090506105a0526104c05160018111614c915760051b6105600180516105a051808203828111614c9157905090508152506105a05160018103818111614c915790506105a0526104c05115613b76576105a051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610600518015614c9157808204905090506105a0525b6105a05160206104c05160018111614c915760051b614d1a016000396000518015614c9157808204905090506105a0526402540be4006105605160605261058051608052613bc56106c0612bfd565b6106c0516105a051808202811583838304141715614c915790509050046106a0526105a0516106a051808203828111614c9157905090506105a052610500516105a0511015613c745760086106c0527f536c6970706167650000000000000000000000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6105c0516105a051808203828111614c9157905090506105c0526105c05160206104c05160018111614c915760051b614d1a01600039600051808202811583838304141715614c9157905090506105c0526104c05115613cf957670de0b6b3a76400006105c05161060051808202811583838304141715614c915790509050046105c0525b6105c0516104c05160018111614c915760051b6105600152610520516080526105405160a0526105605160c0526105805160e0526000610100526106805161012052613d466106c0612d5f565b6106c051610600526105a05181526106a051602082015261060051604082015250565b60405160011c608052604051670de0b6b3a7640000810281670de0b6b3a7640000820418614c915790506060518060011b818160011c18614c915790508015614c91578082049050905060a05260805160a051808202811583838304141715614c9157905090508060b5710100000000000000000000000000000000008210613df9578160801c91508060401b90505b69010000000000000000008210613e17578160401c91508060201b90505b650100000000008210613e31578160201c91508060101b90505b63010000008210613e49578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050815250565b6101a0516060526101c051608052613ece610200612bfd565b6102005160011b60021c6101e05260006102005260006002905b8060051b6101600151610220526102005161022051808201828110614c91579050905061020052600101818118613ee85750506102005160011c6102205260006102405260006002905b8060051b610160015161026052610220516102605111613f705761024051610260516102205103808201828110614c91579050905061024052613f90565b61024051610220516102605103808201828110614c915790509050610240525b600101818118613f325750506101e05161024051808202811583838304141715614c915790509050610200518015614c915780820490509050620186a08101818110614c91579050815250565b601854606051808201828110614c915790509050601855601660405160205260005260406000208054606051808201828110614c91579050905081555060405160007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601854606051808203828111614c915790509050601855601660405160205260005260406000208054606051808203828111614c91579050905081555060006040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b6040516020614d1a600039600051808202811583838304141715614c9157905090508152670de0b6b3a76400006060516020614d3a600039600051808202811583838304141715614c915790509050608051808202811583838304141715614c91579050905004602082015250565b601454610120526201517f61012051420311156141575742600b541161415a565b60015b156141645761451c565b600f546101405260105461016052601854610180526101605161014051111561419b57670de0b6b3a763ffff61018051111561419e565b60015b156141a85761451c565b6141b36101e0612a68565b6101e080516101a05260208101516101c05250600e546101e05260115461020052600154610220526020614dba60003960005163cab4d3db610260526020610260600461027c845afa61420b573d600060003e3d6000fd5b60203d10614c9157610260518060a01c614c91576102a0526102a090505161024052600c5461026052600d54610280526404a817c80061016051610140510364012a05f20081028164012a05f200820418614c91579050046102a0526015546102c05260006102e0526102405115614288576102a051151561428b565b60005b156143575761020051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610200516102a051808203828111614c9157905090508015614c915780820490509050670de0b6b3a76400008103818111614c915790506102e0526102c051610180516102e051808202811583838304141715614c915790509050670de0b6b3a764000081049050808201828110614c9157905090506102c052610140516102a0518060011b818160011c18614c91579050808203828111614c915790509050610140525b610180516102c051808201828110614c915790509050610300526101e0516040526102205160605261438a610320613d69565b61032051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610300518015614c91578082049050905061020052670de0b6b3a763ffff61020051116143d85761451c565b600060155561014051600f5542601455610200516011556101e051610300516101e0516102c051808202811583838304141715614c91579050905004808203828111614c915790509050600e556101605161014051111561443b57610140516010555b604036610320376102c0511561451c5760006002905b80610360526103605160018111614c915760051b61026001516102c051808202811583838304141715614c915790509050610300518015614c9157808204905090506103605160018111614c915760051b6103200152610360516040526103605160018111614c915760051b6103200151606052610240516080526144d46129bb565b600101818118614451575050610240517f3bbd5f2f4711532d6e9ee88dfdf2f1468e9a4c3ae5e14d2e1a67bf4242d008d0610320516103605261034051610380526040610360a25b565b60185461020052610200516101a05111614c915760016101c05111614c9157600c5461022052600d54610240526000610260526001546020614d3a600039600051808202811583838304141715614c91579050905061028052610220516020614d1a600039600051808202811583838304141715614c9157905090506102a052670de0b6b3a76400006102405161028051808202811583838304141715614c915790509050046102c0526101c0516145fe576020614d1a600039600051670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610280525b6101e05161461257600e546102605261467b565b6020614d5a60003960005163e68647666102e052610160516103005261018051610320526102a051610340526102c0516103605260006103805260206102e060a46102fc845afa614668573d600060003e3d6000fd5b60203d10614c91576102e0905051610260525b610260516102e0526102a051610300526102c051610320526101c05160018111614c915760051b6102a001518060011b818160011c18614c915790506101a051808202811583838304141715614c915790509050610200518015614c915780820490509050610340526013546040526146f5610380612bc1565b61038060208101905051610360526101c05160018111614c915760051b610300015161034051101561476d576101c05160018111614c915760051b61030001805161034051808203828111614c9157905090508152506103005160605261032051608052614764610380612bfd565b61038051610360525b610200516101a0516102e051808202811583838304141715614c91579050905004610380526103605161038051808202811583838304141715614c9157905090506404a817c8008104905060018101818110614c915790506103a0526103a0518060011b818160011c18614c915790506101c05160018111614c915760051b6102200151808202811583838304141715614c9157905090506102e0518015614c9157808204905090506103c0526102e051610380516103a051808203828111614c915790509050808203828111614c9157905090506102e0526020614d5a6000396000516343d188fb61040052610160516104205261018051610440526102a051610460526102c051610480526102e0516104a0526101c0516104c052604061040060c461041c845afa6148a6573d600060003e3d6000fd5b60403d10614c91576104009050516103e0526101c05160018111614c915760051b6102a001516103e051808203828111614c915790509050670de0b6b3a7640000810281670de0b6b3a7640000820418614c91579050610280518015614c915780820490509050610400526103e0516101c05160018111614c915760051b6102a001526104005181526102e0516020820152604081016102a05181526102c0516020820152506103c051608082015250565b60805160605160401b60405160801b1717815250565b608051601760405160205260005260406000208060605160205260005260406000209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b6060513081146149d9578015156149dc565b60005b905015614c9157601660405160205260005260406000208054608051808203828111614c915790509050815550601660605160205260005260406000208054608051808201828110614c9157905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b6020614e9a6000396000514614614aed577fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac564726060526020614e7a6080397fd61c1033330c368dfc371f5b1e7133f4794e104642e5a3c87aba7a6a3441c8ff60a0524660c0523060e0526020614eba6101003960c06040526040805160208201209050815250614af6565b6020614eda8239505b565b600254606052600154608052600554604052614b1460c06127aa565b60c05160a0524260a0511015614c885760045460c052601254604052614b3b610100612bc1565b6101006040810190505160e0526020614d5a6000396000516381d18d876101205260a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418614c9157905060e0518015614c9157808204905090508060ff1c614c91577f80000000000000000000000000000000000000000000000000000000000000008114614c9157600003610140526020610120602461013c845afa614be1573d600060003e3d6000fd5b60203d10614c91576101209050516101005260c0516080518060011b818160011c18614c91579050808281188284100218905090506101005180670de0b6b3a764000003670de0b6b3a76400008111614c91579050808202811583838304141715614c91579050905060605161010051808202811583838304141715614c915790509050808201828110614c915790509050670de0b6b3a764000081049050815250614c8f565b6060518152505b565b600080fd167227a4055309ab27a4057323d527a427a40f3127a427a427a405930fd227a4096b0f540d0b08e80e15230427a427a427a416bb27a4051727a405d3236405b327a427a427a41db0233420500c8824f527a423b327a400a627a427a4001a27a427a41dff02670a440dd01e4906d41e93251b08ad04d827a4208009cb098b01051fac27a4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000002005995a71243be9fb995dab4742327dc76564df000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b5204b0be36771253cc38e88012e02b752f0f3600000000000000000000000098ee851a00abee0d95d08cf4ca2bdce32aeaaf7f00000000000000000000000000000000000000000000000000000000000000085a554e2f57455448000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075a554e5745544800000000000000000000000000000000000000000000000000f3f548d5e14bf11b4b0e6d4fb0b6ef0ef910e29ef366bb738c172229c784a6f10000000000000000000000000000000000000000000000000000000000000001a0b5d5a7a022a347f3a45ddc070e84b06ef9ba037d5a354978c1b1b9a082df91aa44218eee738c131da78a6445e2b30df1dc201c163a77c811b9c93f46765eb3
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b5204b0be36771253cc38e88012e02b752f0f360000000000000000000000002005995a71243be9fb995dab4742327dc76564dfa0b5d5a7a022a347f3a45ddc070e84b06ef9ba037d5a354978c1b1b9a082df91000000000000000000000000000000010000000000000000000000000000000100000000000000000000000000061a800000000000000000000083e0717e1000000000000000000000000000018cba800000000002aea5400000d12f0c4c60000000000000000000000001d1a94a2000000084c94623200000000000000003620000000000000000000000000000000000000000000000000000736a356c080000000000000000000000000000000000000000000000000000000000000000085a554e2f5745544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075a554e5745544800000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _name (string): ZUN/WETH
Arg [1] : _symbol (string): ZUNWETH
Arg [2] : _coins (address[2]): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,0x6b5204B0Be36771253Cc38e88012E02B752f0f36
Arg [3] : _math (address): 0x2005995a71243be9FB995DaB4742327dc76564Df
Arg [4] : _salt (bytes32): 0xa0b5d5a7a022a347f3a45ddc070e84b06ef9ba037d5a354978c1b1b9a082df91
Arg [5] : packed_precisions (uint256): 340282366920938463463374607431768211457
Arg [6] : packed_gamma_A (uint256): 136112946768375385385349842972852284582400000
Arg [7] : packed_fee_params (uint256): 8847341539944400050877843276543133320576000000
Arg [8] : packed_rebalancing_params (uint256): 680564733841876929619973849625130958848000000000866
Arg [9] : initial_price (uint256): 126900000000000
-----Encoded View---------------
15 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [1] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 0000000000000000000000006b5204b0be36771253cc38e88012e02b752f0f36
Arg [4] : 0000000000000000000000002005995a71243be9fb995dab4742327dc76564df
Arg [5] : a0b5d5a7a022a347f3a45ddc070e84b06ef9ba037d5a354978c1b1b9a082df91
Arg [6] : 0000000000000000000000000000000100000000000000000000000000000001
Arg [7] : 00000000000000000000000000061a800000000000000000000083e0717e1000
Arg [8] : 000000000000000000000000018cba800000000002aea5400000d12f0c4c6000
Arg [9] : 0000000000000000000001d1a94a2000000084c9462320000000000000000362
Arg [10] : 0000000000000000000000000000000000000000000000000000736a356c0800
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [12] : 5a554e2f57455448000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [14] : 5a554e5745544800000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.