More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 6 from a total of 6 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Exchange | 23459767 | 24 days ago | IN | 0 ETH | 0.00002236 | ||||
Exchange | 23434248 | 27 days ago | IN | 0 ETH | 0.00012152 | ||||
Exchange | 23434246 | 27 days ago | IN | 0 ETH | 0.00012417 | ||||
Remove_liquidity | 23434179 | 27 days ago | IN | 0 ETH | 0.00014384 | ||||
Add_liquidity | 23433804 | 28 days ago | IN | 0 ETH | 0.00036827 | ||||
Add_liquidity | 23433543 | 28 days ago | IN | 0 ETH | 0.00023565 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
0x615c4751 | 23433430 | 28 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xD15fdA1b...567725a39 The constructor portion of the code might be different and could alter the actual behaviour of the contractThis is an ERC-5202 Blueprint contract
Contract Name:
Twocrypto
Compiler Version
vyper:0.4.3
Contract Source Code (Vyper Json-Input format)
# pragma version 0.4.3 # pragma optimize gas """ @title Twocrypto @author Curve.Fi @license Copyright (c) Curve.Fi, 2025 - 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 ethereum.ercs import IERC20 implements: IERC20 # <--------------------- 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 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, n_iter: uint256 ) -> uint256: view # ------------------------------- Events ------------------------------------- event SetPeriphery: views: Views math: Math 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 price_scale: uint256 event AddLiquidity: provider: indexed(address) receiver: indexed(address) token_amounts: uint256[N_COINS] fee: uint256 token_supply: uint256 price_scale: uint256 event Donation: donor: indexed(address) token_amounts: uint256[N_COINS] 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 RemoveLiquidityImbalance: provider: indexed(address) lp_token_amount: uint256 token_amounts: uint256[N_COINS] approx_fee: uint256 price_scale: uint256 event NewParameters: mid_fee: uint256 out_fee: uint256 fee_gamma: uint256 allowed_extra_profit: uint256 adjustment_step: uint256 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] event SetDonationDuration: duration: uint256 event SetDonationProtection: donation_protection_period: uint256 donation_protection_lp_threshold: uint256 donation_shares_max_ratio: uint256 event SetAdminFee: admin_fee: uint256 # ----------------------- 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(Math) VIEW: public(Views) 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. last_prices: public(uint256) last_timestamp: 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. # Donation shares balance donation_shares: public(uint256) donation_shares_max_ratio: public(uint256) # Donations release parameters: donation_duration: public(uint256) last_donation_release_ts: public(uint256) # Donation protection donation_protection_expiry_ts: public(uint256) donation_protection_period: public(uint256) donation_protection_lp_threshold: public(uint256) 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(uint256) MAX_ADMIN_FEE: constant(uint256) = 10**10 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 MIN_RAMP_TIME: constant(uint256) = 86400 MIN_ADMIN_FEE_CLAIM_INTERVAL: constant(uint256) = 86400 A_MULTIPLIER: constant(uint256) = 10000 # Note on pool internal logic: # A is scaled by N_COINS in context of StableswapMath.vy # So A := A_true * N_COINS MIN_A: constant(uint256) = N_COINS * A_MULTIPLIER # to avoid underflow in math (Ann - multiplier) MAX_A: constant(uint256) = 10_000 * A_MULTIPLIER # same as in stableswap MAX_PARAM_CHANGE: constant(uint256) = 10 MIN_GAMMA: constant(uint256) = 10**10 MAX_GAMMA: constant(uint256) = 199 * 10**15 # 1.99 * 10**17 # ----------------------- ERC20 Specific vars -------------------------------- name: public(immutable(String[64])) symbol: public(immutable(String[32])) decimals: public(constant(uint8)) = 18 version: public(constant(String[8])) = "v2.1.0d" balanceOf: public(HashMap[address, uint256]) allowance: public(HashMap[address, HashMap[address, uint256]]) totalSupply: public(uint256) # ----------------------- Contract ------------------------------------------- @deploy def __init__( _name: String[64], _symbol: String[32], _coins: address[N_COINS], _math: address, _salt: bytes32, # not used, left for compatibility with legacy factory packed_precisions: uint256, packed_gamma_A: uint256, packed_fee_params: uint256, packed_rebalancing_params: uint256, initial_price: uint256, ): # these setters must be strreplaced at blueprint deploy time # otherwise pool is unusable until set_periphery is called by admin self.VIEW = Views(0x35048188c02cbc9239e1e5ecb3761eF9dfDcD31f) self.MATH = Math(0x79839c2D74531A8222C0F555865aAc1834e82e51) # this parameter can also be dynamically adjusted at blueprint deployment time self.admin_fee = 0 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, "gamma<MIN" assert gamma_A[0] < MAX_GAMMA+1, "gamma>MAX" assert gamma_A[1] > MIN_A-1, "A<MIN" assert gamma_A[1] < MAX_A+1, "A>MAX" 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 = block.timestamp self.xcp_profit_a = 10**18 self.donation_duration = 7 * 86400 self.donation_protection_expiry_ts = 0 self.donation_protection_period = 10 * 60 # 10 minutes self.donation_protection_lp_threshold = 20 * PRECISION // 100 # 20% self.donation_shares_max_ratio = 10 * PRECISION // 100 # 10% log Transfer(sender=empty(address), receiver=self, value=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 = staticcall IERC20(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, "!coins" # Adjust balances self.balances[_coin_idx] += dx return dx # ----------------------------------------------- ERC20 transferFrom flow. # EXTERNAL CALL assert extcall IERC20(coins[_coin_idx]).transferFrom( sender, self, _dx, default_return_value=True ), "!transferFrom" dx: uint256 = staticcall IERC20(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. @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 extcall IERC20(coins[_coin_idx]).transfer( receiver, _amount, default_return_value=True ), "!transfer" # -------------------------- AMM Main Functions ------------------------------ @external @nonreentrant 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(buyer=msg.sender, sold_id=i, tokens_sold=dx_received, bought_id=j, tokens_bought=out[0], fee=out[1], price_scale=out[2]) return out[0] @external @nonreentrant 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(buyer=msg.sender, sold_id=i, tokens_sold=dx_received, bought_id=j, tokens_bought=out[0], fee=out[1], price_scale=out[2]) return out[0] @internal @view def _donation_shares(_donation_protection: bool = True) -> uint256: """ @notice Calculates the amount of donation shares that are unlocked and not under protection. @dev This function accounts for both time-based release and add_liquidity-based protection. """ donation_shares: uint256 = self.donation_shares if donation_shares == 0: return 0 # --- Time-based release of donation shares --- elapsed: uint256 = block.timestamp - self.last_donation_release_ts unlocked_shares: uint256 = min(donation_shares, donation_shares * elapsed // self.donation_duration) if not _donation_protection: # if donation protection is disabled, return the total amount of unlocked donation shares # this is needed to calculate new timestamp for overlapping donations in add_liquidity # otherwise must always be called with donation_protection=True return unlocked_shares # --- Donation protection damping factor --- protection_factor: uint256 = 0 expiry: uint256 = self.donation_protection_expiry_ts if expiry > block.timestamp: protection_factor = min((expiry - block.timestamp) * PRECISION // self.donation_protection_period, PRECISION) return unlocked_shares * (PRECISION - protection_factor) // PRECISION @external @nonreentrant def add_liquidity( amounts: uint256[N_COINS], min_mint_amount: uint256, receiver: address = msg.sender, donation: bool = False ) -> 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 @param donation Whether the liquidity is a donation, if True receiver is ignored. @return uint256 Amount of LP tokens issued (to receiver or donation buffer). """ assert amounts[0] + amounts[1] > 0, "!amounts" # --------------------- Get prices, balances ----------------------------- old_balances: uint256[N_COINS] = self.balances ########################## TRANSFER IN <------- amounts_received: uint256[N_COINS] = empty(uint256[N_COINS]) # This variable will contain the old balances + the amounts received. balances: uint256[N_COINS] = self.balances for i: uint256 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. ) balances[i] += amounts_received[i] price_scale: uint256 = self.cached_price_scale xp: uint256[N_COINS] = self._xp(balances, price_scale) old_xp: uint256[N_COINS] = self._xp(old_balances, price_scale) # --------------------Finalize ramping of empty pool if self.D == 0: self.future_A_gamma_time = block.timestamp # -------------------- Calculate LP tokens to mint ----------------------- A_gamma: uint256[2] = self._A_gamma() old_D: uint256 = self._get_D(A_gamma, old_xp) D: uint256 = staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) token_supply: uint256 = self.totalSupply d_token: uint256 = 0 if old_D > 0: d_token = token_supply * D // old_D - token_supply else: d_token = self._xcp(D, price_scale) # <----- Making initial virtual price equal to 1. assert d_token > 0, "nothing minted" d_token_fee: uint256 = 0 if old_D > 0: d_token_fee = ( self._calc_token_fee(amounts_received, xp, donation, True) * d_token // 10**10 + 1 ) # for donations - we only take NOISE_FEE (check _calc_token_fee) d_token -= d_token_fee if donation: assert receiver == empty(address), "nonzero receiver" new_donation_shares: uint256 = self.donation_shares + d_token assert new_donation_shares * PRECISION // (token_supply + d_token) <= self.donation_shares_max_ratio, "donation above cap!" # When adding donation, if the previous one hasn't been fully released we preserve # the currently unlocked donation [given by `self._donation_shares()`] by updating # `self.last_donation_release_ts` as if a single virtual donation of size `new_donation_shares` # was made in past and linearly unlocked reaching `self._donation_shares()` at the current time. # We want the following equality to hold: # self._donation_shares() = new_donation_shares * (new_elapsed / self.donation_duration) # We can rearrange this to find the new elapsed time (imitating one large virtual donation): # => new_elapsed = self._donation_shares() * self.donation_duration / new_donation_shares # edge case: if self.donation_shares = 0, then self._donation_shares() is 0 # and new_elapsed = 0, thus initializing last_donation_release_ts = block.timestamp new_elapsed: uint256 = self._donation_shares(False) * self.donation_duration // new_donation_shares # Additional observations: # new_elapsed = (old_pool * old_elapsed / D) * D / new_pool = old_elapsed * (old_pool / new_pool) # => new_elapsed is always smaller than old_elapsed # and self.last_donation_release_ts is carried forward propotionally to new donation size. self.last_donation_release_ts = block.timestamp - new_elapsed # Credit donation: we don't explicitly mint lp tokens, but increase total supply self.donation_shares = new_donation_shares self.totalSupply += d_token log Donation(donor=msg.sender, token_amounts=amounts_received) else: # --- Donation Protection & LP Spam Penalty --- # Extend protection to shield against donation extraction via sandwich attacks. # A penalty is applied for extending the protection to disincentivize spamming. relative_lp_add: uint256 = d_token * PRECISION // (token_supply + d_token) if relative_lp_add > 0 and self.donation_shares > 0: # sub-precision additions are expensive to stack # Extend protection period protection_period: uint256 = self.donation_protection_period extension_seconds: uint256 = min(relative_lp_add * protection_period // self.donation_protection_lp_threshold, protection_period) current_expiry: uint256 = max(self.donation_protection_expiry_ts, block.timestamp) new_expiry: uint256 = min(current_expiry + extension_seconds, block.timestamp + protection_period) self.donation_protection_expiry_ts = new_expiry # Regular liquidity addition self.mint(receiver, d_token) price_scale = self.tweak_price(A_gamma, xp, D) 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 self.mint(receiver, d_token) assert d_token >= min_mint_amount, "slippage" # ---------------------------------------------- Log and claim admin fees. log AddLiquidity( provider=msg.sender, receiver=receiver, token_amounts=amounts_received, fee=d_token_fee, token_supply=token_supply+d_token, price_scale=price_scale ) return d_token @external @nonreentrant 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. @dev This function intentionally does not rely on any external call to the the math contract to make sure that failures in the invariant don't prevent users from withdrawing their funds. @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[N_COINS] Amount of pool tokens received by the `receiver` """ # -------------------------------------------------------- Burn LP tokens. # We cache the total supply to avoid multiple SLOADs. It is important to do # this before the burnFrom call, as the burnFrom call will reduce the supply. total_supply: uint256 = self.totalSupply self.burnFrom(msg.sender, amount) # 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. withdraw_amounts: uint256[N_COINS] = empty(uint256[N_COINS]) D: uint256 = self.D # no ramping adjustment to preserve safety of balanced removal if amount == total_supply: # <----------------------------------- Case 2. for i: uint256 in range(N_COINS): withdraw_amounts[i] = self.balances[i] else: # <-------------------------------------------------------- Case 1. for i: uint256 in range(N_COINS): # TODO improve comments here # Withdraws slightly less -> favors LPs already withdraw_amounts[i] = self.balances[i] * amount // total_supply assert withdraw_amounts[i] >= min_amounts[i], "slippage" # Reduce D proportionally to the amount of tokens leaving. Since withdrawals # are balanced, this is a simple subtraction. If amount == total_supply, # D will be 0. self.D = D - unsafe_div(D * amount, total_supply) # ---------------------------------- Transfers --------------------------- for i: uint256 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) # We intentionally use the unadjusted `amount` here as the amount of lp # tokens burnt is `amount`, regardless of the rounding error. log RemoveLiquidity(provider=msg.sender, token_amounts=withdraw_amounts, token_supply=total_supply - amount) # Take care of leftover donations (only if all LP left) self._withdraw_leftover_donations() return withdraw_amounts @external @nonreentrant def remove_liquidity_fixed_out( token_amount: uint256, i: uint256, amount_i: uint256, min_amount_j: uint256, receiver: address = msg.sender ) -> uint256: """ @notice Withdrawal where amount of token i is specified @param token_amount LP Token amount to burn @param i Index of the coin to withdraw @param amount_i exact amount of token i which will be withdrawn @param min_amount_j Minimum amount of token j=1-i to withdraw. @param receiver Address to send the withdrawn tokens to @return Amount of tokens at index j=1-i received by the `receiver` """ return self._remove_liquidity_fixed_out( token_amount, i, amount_i, min_amount_j, receiver, ) @external @nonreentrant def remove_liquidity_one_coin( lp_token_amount: uint256, i: uint256, min_amount: uint256, receiver: address = msg.sender ) -> uint256: """ @notice Withdraw liquidity in a single coin. @param lp_token_amount Amount of LP tokens to burn. @param i Index of the coin to withdraw. @param min_amount Minimum amount of coin[i] to withdraw. @param receiver Address to send the withdrawn tokens to @return Amount of coin[i] tokens received by the `receiver` """ return self._remove_liquidity_fixed_out( lp_token_amount, 1 - i, # Here we flip i because we want to constrain the other coin to be zero. 0, # We set the amount of coin[1 - i] to be withdrawn to 0. min_amount, receiver, ) @internal def _remove_liquidity_fixed_out( token_amount: uint256, i: uint256, amount_i: uint256, min_amount_j: uint256, receiver: address, ) -> uint256: self._claim_admin_fees() A_gamma: uint256[2] = self._A_gamma() # Amount of coin[j] withdrawn. dy: uint256 = 0 # New value of D after the withdrawal. D: uint256 = 0 # New scaled balances after the withdrawal. xp: uint256[N_COINS] = empty(uint256[N_COINS]) approx_fee: uint256 = 0 # ------------------------------------------------------------------------ dy, D, xp, approx_fee = self._calc_withdraw_fixed_out( A_gamma, token_amount, i, amount_i, ) assert dy >= min_amount_j, "slippage" # ---------------------------- State Updates ----------------------------- self.burnFrom(msg.sender, token_amount) price_scale: uint256 = self.tweak_price(A_gamma, xp, D) if amount_i != 0: # one-sided withdrawals call with amount_i = 0, save extcall here self._transfer_out(i, amount_i, receiver) j: uint256 = 1 - i self._transfer_out(j, dy, receiver) token_amounts: uint256[N_COINS] = empty(uint256[N_COINS]) token_amounts[i] = amount_i token_amounts[j] = dy if amount_i == 0: log RemoveLiquidityOne( provider=msg.sender, token_amount=token_amount, coin_index=j, coin_amount=dy, approx_fee=approx_fee * token_amount // 10**10 + 1, # LP units, not coins! packed_price_scale=price_scale ) else: log RemoveLiquidityImbalance( provider=msg.sender, lp_token_amount=token_amount, token_amounts=token_amounts, approx_fee=approx_fee * token_amount // 10**10 + 1, # LP units price_scale=price_scale ) # Take care of leftover donations (only if all LP left) self._withdraw_leftover_donations() return dy @internal def _withdraw_leftover_donations(): """ @notice Withdraws leftover donations from the pool. This is called when the pool has no other liquidity than donation shares, and must be emptied. @dev donations go to the factory fees receiver, if not set, to the admin. """ if self.donation_shares != self.totalSupply: return # Pool has no other LP than donation shares, must be emptied receiver: address = staticcall factory.fee_receiver() if receiver == empty(address): receiver = staticcall factory.admin() # empty the pool withdraw_amounts: uint256[N_COINS] = self.balances for i: uint256 in range(N_COINS): # updates self.balances here self._transfer_out(i, withdraw_amounts[i], receiver) # Update state self.donation_shares = 0 self.totalSupply = 0 self.D = 0 self.donation_protection_expiry_ts = 0 log RemoveLiquidity(provider=receiver, token_amounts=withdraw_amounts, token_supply=0) # -------------------------- 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] @internal def _exchange( i: uint256, j: uint256, dx_received: uint256, min_dy: uint256, ) -> uint256[3]: assert i != j, "same coin" assert dx_received > 0, "zero dx" A_gamma: uint256[2] = self._A_gamma() balances: uint256[N_COINS] = self.balances dy: uint256 = 0 y: uint256 = balances[j] x0: uint256 = balances[i] - dx_received # old xp[i] price_scale: uint256 = self.cached_price_scale xp: uint256[N_COINS] = self._xp(balances, price_scale) # ----------- Update invariant if A, gamma are undergoing ramps --------- if self._is_ramping(): 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 = staticcall self.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] = staticcall self.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 ---------- # Technically a swap wouldn't require to recompute D, however since we're taking # fees, we need to update D to reflect the new balances. D = staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, y_out[1]) price_scale = self.tweak_price(A_gamma, xp, D) return [dy, fee, price_scale] @internal def tweak_price( A_gamma: uint256[2], _xp: uint256[N_COINS], D: uint256, ) -> 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_fixed_out. @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 D New D value. @return uint256 The new price_scale. """ # ---------------------------- 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) is_ramping: bool = self._is_ramping() # store as we bump the timestamp below # Contains: allowed_extra_profit, adjustment_step, ma_time. -----^ # ------------------ Update Price Oracle if needed ----------------------- last_timestamp: uint256 = self.last_timestamp alpha: uint256 = 0 if last_timestamp < 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 = staticcall self.MATH.wad_exp( -convert( unsafe_div( unsafe_sub(block.timestamp, last_timestamp) * 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 self.last_timestamp = block.timestamp # `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. # ------------------------------------------------------------------------ # Here we update the spot price, please notice that this value is unsafe # and can be manipulated. self.last_prices = unsafe_div( staticcall self.MATH.get_p(_xp, D, A_gamma) * price_scale, 10**18 ) # ---------- Update profit numbers without price adjustment first -------- # `totalSupply` might change during this function call. total_supply: uint256 = self.totalSupply # ===== donation shares (time release + add_liquidity throttling) ===== donation_shares: uint256 = self._donation_shares() # locked_supply contains LP shares and unreleased donations locked_supply: uint256 = total_supply - donation_shares old_virtual_price: uint256 = self.virtual_price xcp: uint256 = self._xcp(D, price_scale) virtual_price: uint256 = 10**18 * xcp // total_supply # Virtual price can decrease only if A and gamma are being ramped. # This does not imply that the virtual price will have increased at the # end of this function: it can still decrease if the pool rebalances. if virtual_price < old_virtual_price: # If A and gamma are being ramped, we allow the virtual price to decrease, # as changing the shape of the bonding curve causes losses in the pool. assert is_ramping, "virtual price decreased" # xcp_profit follows growth of virtual price (and goes down on ramping) xcp_profit: uint256 = self.xcp_profit + virtual_price - old_virtual_price self.xcp_profit = xcp_profit # ------------ Rebalance liquidity if there's enough profits to adjust it: # # Mathematical basis for rebalancing condition: # 1. xcp_profit grows after virtual price, total growth since launch = (xcp_profit − 1) # 2. We reserve half of the growth for LPs and admin, rest is used to rebalance the pool # Rebalancing condition transformation: # virtual_price - 1 > (xcp_profit - 1)/2 + allowed_extra_profit # virtual_price > 1 + (xcp_profit - 1)/2 + allowed_extra_profit threshold_vp: uint256 = max(10**18, (xcp_profit + 10**18) // 2) # The allowed_extra_profit parameter prevents reverting gas-wasting rebalances # by ensuring sufficient profit margin # user_supply < total_supply => vp_boosted > virtual_price # by not accounting for donation shares, virtual_price is boosted leading to rebalance trigger # this is approximate condition that preliminary indicates readiness for rebalancing vp_boosted: uint256 = 10**18 * xcp // locked_supply assert vp_boosted >= virtual_price, "negative donation" if (vp_boosted > threshold_vp + rebalancing_params[0]) and (last_timestamp < block.timestamp): # allowed_extra_profit --------^ # ^ only rebalance once per block (first tx) 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. # 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. if norm > adjustment_step: # 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: uint256[N_COINS] = [ _xp[0], unsafe_div(_xp[1] * p_new, price_scale) ] # ------------------------------------------ Update D with new xp. new_D: uint256 = staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) # --------------------------------------------- Calculate new xcp. new_xcp: uint256 = self._xcp(new_D, p_new) new_virtual_price: uint256 = 10**18 * new_xcp // total_supply donation_shares_to_burn: uint256 = 0 # burn donations to get to old vp, but not below threshold_vp goal_vp: uint256 = max(threshold_vp, virtual_price) if new_virtual_price < goal_vp: # new_virtual_price is lower than virtual_price. # We attempt to boost virtual_price by burning some donation shares # This will result in more frequent rebalances. # # vp(0) = xcp / total_supply # no burn -> lowest vp # vp(B) = xcp / (total_supply – B) # burn B -> higher vp # # Goal: find the *smallest* B such that # vp(B) -> virtual_price (pre-rebalance value) # B <= donation_shares # what would be total supply with (old) virtual_price and new_xcp tweaked_supply: uint256 = 10**18 * new_xcp // goal_vp assert tweaked_supply < total_supply, "tweaked supply must shrink" donation_shares_to_burn = min( unsafe_sub(total_supply, tweaked_supply), # burn the difference between supplies donation_shares # but not more than we can burn (lp shares donation) ) # update virtual price with the tweaked total supply new_virtual_price = 10**18 * new_xcp // (total_supply - donation_shares_to_burn) # we thus burn some donation shares to compensate for virtual price drop if ( new_virtual_price > 10**18 and new_virtual_price >= threshold_vp # only rebalance when pool preserves half of the profits ): self.D = new_D self.virtual_price = new_virtual_price self.cached_price_scale = p_new if donation_shares_to_burn > 0: # Invariant to hold immediately after the burn (measured after protection): # _donation_shares()' = _donation_shares() - donation_shares_to_burn # We shoud carry forward self.last_donation_release_ts to satisfy the invariant # Get pre-burn state: shares_unlocked: uint256 = self._donation_shares(False) # time‑unlocked, ignores protection shares_available: uint256 = donation_shares # available after protection (computed above self._donation_shares(True)) # Invariant: shares_available_new = shares_available - donation_shares_to_burn # Definition: shares_available = shares_unlocked * (1 - protection) [Note: (1 - protection) = shares_available / shares_unlocked] # To reduce shares_available_new by donation_shares_to_burn (B), we should reduce the shares_unlocked_new proportionally: # shares_available_new = shares_available - B = shares_unlocked * (1 - protection) - B = (shares_unlocked - B/(1 - protection)) * (1 - protection) # => shares_unlocked_new = shares_unlocked - B/(1 - protection) = shares_unlocked - B * shares_unlocked / shares_available shares_unlocked_new: uint256 = shares_unlocked - donation_shares_to_burn * shares_unlocked // shares_available # Definition: shares_unlocked_new = new_total * new_elapsed // donation_duration # => new_elapsed = shares_unlocked_new * donation_duration // new_total new_total: uint256 = self.donation_shares - donation_shares_to_burn new_elapsed: uint256 = 0 if new_total > 0 and shares_unlocked_new > 0: new_elapsed = (shares_unlocked_new * self.donation_duration) // new_total # Apply the burn: update the state and shift the release timestamp self.donation_shares = new_total self.totalSupply -= donation_shares_to_burn self.last_donation_release_ts = block.timestamp - new_elapsed return p_new # If we end up here price_scale was not adjusted. So we update the state # with the virtual price and D we calculated before attempting a rebalance. self.D = D 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._is_ramping() ): 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. ---------- # no _get_D() because we can't claim during ramping D: uint256 = self.D vprice: uint256 = self.virtual_price price_scale: uint256 = self.cached_price_scale fee_receiver: address = staticcall 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, stored in self.admin_fee (with 10**10 precision). # 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) * self.admin_fee, 2 * 10**10 ) # ------------------------------ Claim admin fees by minting admin's share # of the pool in LP tokens. admin_share: uint256 = 0 if fee_receiver != empty(address) and fees > 0: # -------------------------------- Calculate admin share to be minted. frac: uint256 = vprice * 10**18 // (vprice - fees) - 10**18 admin_share += current_lp_token_supply * frac // 10**18 # When claiming fees, the virtual price decreases: # Let TS = total_supply, f = fees # vp' = xcp/(TS + TS*((vp/vp-f) - 1)) = (xcp/TS) / (1 + f/(vp-f)) = # = vp / (vp / (vp-f)) = (vp-f) # vp' = (vp-f) # Thus, to maintain the condition vp' - 1 > (xcp_profit' - 1)/2: # xcp_profit' := xcp_profit - 2 * f xcp_profit -= fees * 2 # Another way to look at it - we either track admin_claimed_xcp (=sum(fees)), # and always use it to calculate admin+LP reserve, or just -=2*fees in xcp_profit. # xcp_profit as raw value is thus should't be used in integrations! # ------------------- Recalculate virtual_price following admin fee claim. total_supply_including_admin_share: uint256 = ( current_lp_token_supply + admin_share ) vprice = ( 10**18 * self._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 ------------------------------ 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: uint256 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(admin=fee_receiver, tokens=admin_tokens) @internal @view 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) ] @external @view def user_supply() -> uint256: """ @notice Returns the amount of LP tokens that are not locked in donations. @return uint256 Amount of LP tokens that are not locked in donations. """ return self.totalSupply - self.donation_shares @internal @view def _is_ramping() -> bool: """ @notice Checks if A and gamma are ramping. @return bool True if A and/or gamma are ramping, False otherwise. """ return self.future_A_gamma_time > self.last_timestamp @internal @view def _check_admin(): assert msg.sender == staticcall factory.admin(), "only owner" @internal @view 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: # unpack mid_fee, out_fee, fee_gamma fee_params: uint256[3] = self._unpack_3(self.packed_fee_params) # warm up variable with sum of balances B: uint256 = xp[0] + xp[1] # balance indicator that goes from 10**18 (perfect pool balance) to 0 (very imbalanced, 100:1 and worse) # N^N * (xp[0] * xp[1]) / (xp[0] + xp[1])**2 B = PRECISION * N_COINS**N_COINS * xp[0] // B * xp[1] // B # regulate slope using fee_gamma # fee_gamma * balance_term / (fee_gamma * balance_term + 1 - balance_term) B = fee_params[2] * B // (unsafe_div(fee_params[2] * B, 10**18) + 10**18 - B) # mid_fee * B + out_fee * (1 - B) return unsafe_div(fee_params[0] * B + fee_params[1] * (10**18 - B), 10**18) @internal @view def _get_D(A_gamma: uint256[2], xp: uint256[N_COINS]) -> uint256: # Normally we need self.D, however, if A and/or gamma are ramping, # we need to recalculate D using the current A and gamma values. if self._is_ramping(): # ongoing ramping, recalculate D return staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) else: # not ramping, use self.D from storage return self.D @internal @pure def _xcp(D: uint256, price_scale: uint256) -> uint256: # We compute xcp according to the formula in the whitepaper: # The following explanation relies on the assumption that the # balances have already been scaled by the price scale as shown # above. # The intuition behind this formula comes from the UniV2 # whitepaper where the initial amount of LP tokens is set to # the geometric mean of the balances, in fact xcp stands for # x (balances) constant product. # Our invariant behaves in such a way that at the center of the # bonding curve: # (1) D(x, y) = D(x, x) = 2x. # In simple terms this mean that at the center the pool behaves exactly # like a constant sum AMM. # Here we want to treat the pool as a constant product AMM: # (2) xy = k (the constant product invariant). # (3) x^2 = k (because we are at the center of the curve where x = y). # (4) x = D / 2 (because D(x, y) = 2x in (1]). # For xp[0] the price scale is 1 (see whitepaper) so we can obtain # x[0] directly from [4] # For xp[1] the price scale is != 1 so we divide by the price scale # that has unit (coin0/coin1) to convert D (coin0) into xp[1] (coin1): # (5) x[1] = D / 2 / price_scale. # In the end we take the geometric average of the scaled balances: # xcp = sqrt(D // (N_COINS * 1) * D // (N_COINS * price_scale)) # this is equivalent to D // N_COINS * sqrt(price_scale). return D * PRECISION // N_COINS // isqrt(PRECISION * price_scale) @internal @view def _calc_token_fee(amounts: uint256[N_COINS], xp: uint256[N_COINS], donation: bool = False, deposit: bool = False, from_view: bool = False) -> uint256: if donation: # Donation fees are 0, but NOISE_FEE is required for numerical stability return NOISE_FEE surplus_amounts: uint256[N_COINS] = amounts if from_view: # When calling from the view contract no liquidity has been # added to the balances. surplus_amounts = [0, 0] # the ratio of the balances before the liquidity operation # balances[0] / balances[1] (adjusted for fixed precisions) balances_ratio: uint256 = (self.balances[0] - surplus_amounts[0]) * PRECISIONS[0] * PRECISION // ((self.balances[1] - surplus_amounts[1]) * PRECISIONS[1]) # We calculate the fee based on the impact on the spot balances. # For this reason here (AND ONLY HERE) we use the balances ratio and not # the price_scale in self._xp(). amounts = self._xp(amounts, balances_ratio) # fee = sum(amounts_i - avg(amounts)) * fee' / sum(amounts) # fee' = _fee(xp) * N_COINS / (4 * (N_COINS - 1)) = _fee(xp)/2 (for N_COINS=2) fee: uint256 = unsafe_div( unsafe_mul(self._fee(xp), N_COINS), unsafe_mul(4, unsafe_sub(N_COINS, 1)) ) S: uint256 = 0 for _x: uint256 in amounts: S += _x avg: uint256 = unsafe_div(S, N_COINS) Sdiff: uint256 = 0 for _x: uint256 in amounts: if _x > avg: Sdiff += unsafe_sub(_x, avg) else: Sdiff += unsafe_sub(avg, _x) lp_spam_penalty_fee: uint256 = 0 if deposit: # Penalty fee for spamming add_liquidity into the pool current_expiry: uint256 = self.donation_protection_expiry_ts if current_expiry > block.timestamp: # The penalty is proportional to the remaining protection time and the current pool fee. protection_factor: uint256 = min((current_expiry - block.timestamp) * PRECISION // self.donation_protection_period, PRECISION) lp_spam_penalty_fee = protection_factor * fee // PRECISION return fee * Sdiff // S + NOISE_FEE + lp_spam_penalty_fee @view @external def calc_withdraw_fixed_out(lp_token_amount: uint256, i: uint256, amount_i: uint256) -> uint256: """ @notice Calculate the amounts of coin[1-i] that will be received for burning the lp tokens while specifying the amount of coin[i] to be withdrawn. @param lp_token_amount LP Token amount to burn. @param i index of the token for which the withdrawal amount is specified. @param amount_i exact amount of token i which will be withdrawn. @return uint256 Amount of token 1-i received for burning token_amount LP tokens. """ return self._calc_withdraw_fixed_out( self._A_gamma(), lp_token_amount, i, amount_i, )[0] @view @external def calc_withdraw_one_coin(lp_token_amount: uint256, i: uint256) -> uint256: """ @notice Calculate how much of coin[i] will be received when withdrawing liquidity in a single coin. @dev This function uses the logic from _calc_withdraw_fixed_out by setting amount_i to 0. This forces the withdrawal to be entirely in the other coin. @param lp_token_amount LP Token amount to burn. @param i index of the token to be withdrawn @return uint256 Amount of coin[i] tokens received for burning token_amount LP tokens. """ return self._calc_withdraw_fixed_out( self._A_gamma(), lp_token_amount, 1 - i, # Here we flip i because we want to constrain the other coin to be zero. 0, # We set the amount of coin[1 - i] to be withdrawn to 0. )[0] @internal @view def _calc_withdraw_fixed_out( A_gamma: uint256[2], lp_token_amount: uint256, i: uint256, amount_i: uint256, ) -> (uint256, uint256, uint256[N_COINS], uint256): """ Withdraws specified number of LP tokens while amount of coin `i` is also specified """ token_supply: uint256 = self.totalSupply assert lp_token_amount <= token_supply, "!amount" # Since N_COINS = 2, we don't need to check if i < N_COINS # because j = 1 - i will underflow for any i > 1 j: uint256 = 1 - i balances: uint256[N_COINS] = self.balances # -------------------------- Calculate D0 and xp ------------------------- price_scale: uint256 = self.cached_price_scale xp: uint256[N_COINS] = self._xp(balances, price_scale) D: uint256 = self._get_D(A_gamma, xp) # We adjust D not to take into account any donated amount. Donations # should never be withdrawable by the LPs. # ------------------------------ Amounts calc ---------------------------- dD: uint256 = unsafe_div(lp_token_amount * D, token_supply) xp_new: uint256[N_COINS] = xp price_scales: uint256[N_COINS] = [PRECISION * PRECISIONS[0], price_scale * PRECISIONS[1]] # amountsp (amounts * p) is the dx and dy amounts that the user will receive # after the withdrawal scaled for the price scale (p). amountsp: uint256[N_COINS] = empty(uint256[N_COINS]) # This withdrawal method fixes the amount of token i to be withdrawn, # this is why here we don't compute amountsp[i] but we give it as a # constraint (after appropriate scaling). amountsp[i] = unsafe_div(amount_i * price_scales[i], PRECISION) xp_new[i] -= amountsp[i] # We compute the position on the y axis after a withdrawal of dD with the constraint # that xp_new[i] has been reduced by amountsp[i]. This is the new position on the curve # after the withdrawal without applying fees. y: uint256 = (staticcall self.MATH.get_y(A_gamma[0], A_gamma[1], xp_new, D - dD, j))[0] amountsp[j] = xp[j] - y xp_new[j] = y # _calc_token_fee expects unscaled amounts and without decimals # adjustments. amounts: uint256[N_COINS] = empty(uint256[N_COINS]) amounts[i] = amount_i if i == 0: amounts[1] = amountsp[1] * PRECISION // PRECISIONS[1] // price_scale else: amounts[0] = amountsp[0] // PRECISIONS[0] assert amounts[0] + amounts[1] > 0, "!tokens" # The only way to compute the fees is to simulate a withdrawal as we have done # above and then rewind and apply the fees. approx_fee: uint256 = self._calc_token_fee(amounts, xp_new) dD -= dD * approx_fee // 10**10 + 1 # Same reasoning as before except now we're charging fees. y = (staticcall self.MATH.get_y(A_gamma[0], A_gamma[1], xp_new, D - dD, j))[0] # We descale y to obtain the amount dy in balances and not scaled balances. dy: uint256 = (xp[j] - y) * PRECISION // price_scales[j] xp_new[j] = y return dy, D - dD, xp_new, approx_fee # ------------------------ ERC20 functions ----------------------------------- @internal def _approve(_owner: address, _spender: address, _value: uint256): self.allowance[_owner][_spender] = _value log Approval(owner=_owner, spender=_spender, value=_value) @internal def _transfer(_from: address, _to: address, _value: uint256): assert _to not in [self, empty(address)], "!receiver" self.balanceOf[_from] -= _value self.balanceOf[_to] += _value log Transfer(sender=_from, receiver=_to, value=_value) @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 @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(sender=empty(address), receiver=_to, value=_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(sender=_to, receiver=empty(address), value=_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.last_timestamp 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 = staticcall self.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 staticcall factory.fee_receiver() @external @view def admin() -> address: """ @notice Returns the address of the pool's admin. @return address Admin. """ return staticcall 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. """ return staticcall self.VIEW.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. """ return staticcall self.VIEW.get_dy(i, j, dx, self) @external @view def get_dx(i: uint256, j: uint256, dy: uint256, n_iter: uint256 = 5) -> 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 @param n_iter number of iterations to run @return uint256 Approximate amount of input i tokens to get dy amount of j tokens. """ return staticcall self.VIEW.get_dx(i, j, dy, self, n_iter) @external @view @nonreentrant 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 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._xcp(self.D, self.cached_price_scale) // self.totalSupply @external @view @nonreentrant 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 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)) @external @view def calc_token_fee( amounts: uint256[N_COINS], xp: uint256[N_COINS], donation: bool = False, deposit: bool = False ) -> uint256: """ @notice Returns the fee charged on the given amounts for add_liquidity. @param amounts The amounts of coins being added to the pool (unscaled). @param xp The current balances of the pool multiplied by coin precisions. @param donation Whether the liquidity is a donation, if True only NOISE_FEE is charged. @param deposit Whether the liquidity is a deposit. @return uint256 Fee charged. """ # last True is for from_view return self._calc_token_fee(amounts, xp, donation, deposit, True) @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) # ------------------------- 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. """ self._check_admin() assert not self._is_ramping(), "!ramp" assert future_time > block.timestamp + MIN_RAMP_TIME - 1, "ramp time<min" 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, "A<min" assert future_A < MAX_A + 1, "A>max" assert future_gamma > MIN_GAMMA - 1, "gamma<min" assert future_gamma < MAX_GAMMA + 1, "gamme>max" ratio: uint256 = 10**18 * future_A // A_gamma[0] assert ratio < 10**18 * MAX_PARAM_CHANGE + 1, "A too high" assert ratio > 10**18 // MAX_PARAM_CHANGE - 1, "A too low" ratio = 10**18 * future_gamma // A_gamma[1] assert ratio < 10**18 * MAX_PARAM_CHANGE + 1, "gamma too high" assert ratio > 10**18 // MAX_PARAM_CHANGE - 1, "gamma too low" 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( initial_A=A_gamma[0], future_A=future_A, initial_gamma=A_gamma[1], future_gamma=future_gamma, initial_time=block.timestamp, future_time=future_time ) @external def stop_ramp_A_gamma(): """ @notice Stop Ramping A and gamma parameters immediately. @dev Only accessible by factory admin. """ self._check_admin() 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(current_A=A_gamma[0], current_gamma=A_gamma[1], time=block.timestamp) @external @nonreentrant 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, ): """ @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). """ self._check_admin() # ----------------------------- 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, "!fee" 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, "!mid-fee" if new_fee_gamma < 10**18: assert new_fee_gamma > 0, "!fee_gamma" 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, "MA<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] ) # ---------------------------------- LOG --------------------------------- log NewParameters( mid_fee=new_mid_fee, out_fee=new_out_fee, fee_gamma=new_fee_gamma, allowed_extra_profit=new_allowed_extra_profit, adjustment_step=new_adjustment_step, ma_time=new_ma_time ) @external def set_donation_duration(duration: uint256): """ @notice Set the donation duration. @param duration The new donation duration. @dev The time required for donations to fully release from locked state. """ self._check_admin() assert duration > 0, "!duration" self.donation_duration = duration log SetDonationDuration(duration=duration) @external def set_donation_protection_params( _period: uint256, _threshold: uint256, _max_shares_ratio: uint256, ): """ @notice Set donation protection parameters. @param _period The new donation protection period in seconds. @param _threshold The new donation protection threshold with 10**18 precision. @param _max_shares_ratio The new maximum number of shares. @dev _threshold = 30 * 10**18//100 means 30% @dev _max_shares_ratio = 10 * 10**18//100 means 10% """ self._check_admin() assert _period > 0, "!period" assert _threshold > 0, "!threshold" assert _max_shares_ratio > 0, "!max_shares" self.donation_protection_period = _period self.donation_protection_lp_threshold = _threshold self.donation_shares_max_ratio = _max_shares_ratio log SetDonationProtection( donation_protection_period=_period, donation_protection_lp_threshold=_threshold, donation_shares_max_ratio=_max_shares_ratio ) @external def set_admin_fee(admin_fee: uint256): """ @notice Set the admin fee. @param admin_fee The new admin fee. @dev The admin fee is a percentage of the profits that are claimed by the admin. The fee is set in bps. """ self._check_admin() assert admin_fee <= MAX_ADMIN_FEE, "admin_fee>MAX" self.admin_fee = admin_fee log SetAdminFee(admin_fee=admin_fee) @external def set_periphery(views: Views, math: Math): """ @notice Set the view contract. @param views The new view contract. @dev This function is used to set the view contract that will be used to calculate the prices and fees. """ self._check_admin() # at least one of the two must be set assert views != empty(Views) or math != empty(Math), "!contract" if views != empty(Views): self.VIEW = views if math != empty(Math): self.MATH = math log SetPeriphery(views=views, math=math)
{ "outputSelection": { "<unknown>": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] }, "search_paths": [ "." ] }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"name":"views","type":"address"},{"indexed":false,"name":"math","type":"address"}],"name":"SetPeriphery","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"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":"price_scale","type":"uint256"}],"name":"TokenExchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"},{"indexed":false,"name":"fee","type":"uint256"},{"indexed":false,"name":"token_supply","type":"uint256"},{"indexed":false,"name":"price_scale","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"donor","type":"address"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"}],"name":"Donation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"},{"indexed":false,"name":"token_supply","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":false,"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"}],"name":"RemoveLiquidityOne","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":false,"name":"lp_token_amount","type":"uint256"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"},{"indexed":false,"name":"approx_fee","type":"uint256"},{"indexed":false,"name":"price_scale","type":"uint256"}],"name":"RemoveLiquidityImbalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"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"}],"name":"NewParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"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"}],"name":"RampAgamma","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"current_A","type":"uint256"},{"indexed":false,"name":"current_gamma","type":"uint256"},{"indexed":false,"name":"time","type":"uint256"}],"name":"StopRampA","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"admin","type":"address"},{"indexed":false,"name":"tokens","type":"uint256[2]"}],"name":"ClaimAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"duration","type":"uint256"}],"name":"SetDonationDuration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"donation_protection_period","type":"uint256"},{"indexed":false,"name":"donation_protection_lp_threshold","type":"uint256"},{"indexed":false,"name":"donation_shares_max_ratio","type":"uint256"}],"name":"SetDonationProtection","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin_fee","type":"uint256"}],"name":"SetAdminFee","type":"event"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"name":"exchange","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"receiver","type":"address"}],"name":"exchange","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"name":"exchange_received","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"receiver","type":"address"}],"name":"exchange_received","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"}],"name":"add_liquidity","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"},{"name":"receiver","type":"address"}],"name":"add_liquidity","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"},{"name":"receiver","type":"address"},{"name":"donation","type":"bool"}],"name":"add_liquidity","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"}],"name":"remove_liquidity","outputs":[{"name":"","type":"uint256[2]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"},{"name":"receiver","type":"address"}],"name":"remove_liquidity","outputs":[{"name":"","type":"uint256[2]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"amount_i","type":"uint256"},{"name":"min_amount_j","type":"uint256"}],"name":"remove_liquidity_fixed_out","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"amount_i","type":"uint256"},{"name":"min_amount_j","type":"uint256"},{"name":"receiver","type":"address"}],"name":"remove_liquidity_fixed_out","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"}],"name":"remove_liquidity_one_coin","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"},{"name":"receiver","type":"address"}],"name":"remove_liquidity_one_coin","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"user_supply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"amount_i","type":"uint256"}],"name":"calc_withdraw_fixed_out","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"}],"name":"calc_withdraw_one_coin","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee_receiver","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"deposit","type":"bool"}],"name":"calc_token_amount","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"}],"name":"get_dy","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dy","type":"uint256"}],"name":"get_dx","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dy","type":"uint256"},{"name":"n_iter","type":"uint256"}],"name":"get_dx","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lp_price","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"get_virtual_price","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price_oracle","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price_scale","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"xp","type":"uint256[2]"}],"name":"calc_token_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"xp","type":"uint256[2]"},{"name":"donation","type":"bool"}],"name":"calc_token_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"xp","type":"uint256[2]"},{"name":"donation","type":"bool"},{"name":"deposit","type":"bool"}],"name":"calc_token_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"A","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mid_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"out_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee_gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowed_extra_profit","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adjustment_step","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ma_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"precisions","outputs":[{"name":"","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"xp","type":"uint256[2]"}],"name":"fee_calc","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"future_A","type":"uint256"},{"name":"future_gamma","type":"uint256"},{"name":"future_time","type":"uint256"}],"name":"ramp_A_gamma","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stop_ramp_A_gamma","outputs":[],"stateMutability":"nonpayable","type":"function"},{"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":"apply_new_parameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"duration","type":"uint256"}],"name":"set_donation_duration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_period","type":"uint256"},{"name":"_threshold","type":"uint256"},{"name":"_max_shares_ratio","type":"uint256"}],"name":"set_donation_protection_params","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"admin_fee","type":"uint256"}],"name":"set_admin_fee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"views","type":"address"},{"name":"math","type":"address"}],"name":"set_periphery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"MATH","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VIEW","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"coins","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_prices","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_timestamp","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initial_A_gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initial_A_gamma_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"future_A_gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"future_A_gamma_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_shares","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_shares_max_ratio","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_duration","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_donation_release_ts","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_protection_expiry_ts","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_protection_period","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_protection_lp_threshold","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"D","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xcp_profit","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xcp_profit_a","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"virtual_price","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"packed_rebalancing_params","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"packed_fee_params","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"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":"constructor"}]
Contract Creation Code
0x615c47515034610483576020615fe75f395f51602081615fe7015f395f51604081116104835750606081615fe7016060395060206160075f395f51602081615fe7015f395f51602081116104835750604081615fe70160c0395060206160275f395f518060a01c610483576101005260206160475f395f518060a01c610483576101205260206160675f395f518060a01c61048357610140527335048188c02cbc9239e1e5ecb3761ef9dfdcd31f6001557379839c2d74531a8222c0f555865aac1834e82e515f555f60195533615ba7526020606051015f81601f0160051c6003811161048357801561010d57905b8060051b606001518160051b60a001615b2701526001018181186100ee575b50505060c051615c275260e051615c475261010051615b675261012051615b875260206160a760403961014161016061045e565b6101608051615b27526020810151615b47525060206160c76040396101676101a061045e565b6101a06040816101605e506402540be4006101605110156101fa576020806102005260096101a0527f67616d6d613c4d494e00000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6702c2fd72164d8000610160511115610285576020806102005260096101a0527f67616d6d613e4d415800000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b614e2061018051101561030a576020806102005260056101a0527f413c4d494e0000000000000000000000000000000000000000000000000000006101c0526101a08161020001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6305f5e100610180511115610391576020806102005260056101a0527f413e4d41580000000000000000000000000000000000000000000000000000006101c0526101a08161020001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b60206160c75f395f5160065560206160c75f395f5160085560206161075f395f5160175560206160e75f395f5160185560206161275f395f5160025560206161275f395f5160035560206161275f395f5160045542600555670de0b6b3a764000060155562093a80600c555f600e55610258600f556702c68af0bb14000060105567016345785d8a0000600b55305f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f6101a05260206101a0a3615b2761048761000039615c67610000f35b6fffffffffffffffffffffffffffffffff60405116815260405160801c602082015250565b5f80fd5f3560e01c60026048820660011b615a9701601e395f51565b635b41b908811861003757608436103417615a935733610900526100aa565b63d5f8da308118612c4d57606436103417615a93576020610059610760612f24565b6107606040816108405e50606060046108803760a06108406103805e6100806107a0614e6a565b6107a0f35b63a64833a081186101785760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d600435604052604435606052336080525f60a0526100d6610940612c51565b610940516109205260406004610600376109205161064052606435610660526101006109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526101276143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b633dd654788118612c4d5734615a935760175460405260206040f35b6329b244bb8118612c4d57608436103417615a935733610900526101d8565b63767691e781186102a75760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d60043560405260443560605233608052600160a052610205610940612c51565b6109405161092052604060046106003761092051610640526064356106605261022f6109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526102566143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b6348155d27811861030757608436103417615a93576064356040525b60206001546333e39968606052606060046080373060e052604051610100526020606060a4607c845afa6102f9573d5f5f3e3d5ffd5b60203d10615a935760609050f35b63f9ed95978118612c4d5734615a935760095460405260206040f35b630b4c7e4d811861034757606436103417615a935733610600525f610620526103f1565b63083812e58118612c4d5734615a9357602060175460405261036960606130f4565b6060602081019050f35b630c3e4b5481186103a157608436103417615a93576064358060a01c615a9357610600525f610620526103f1565b63c146bf948118612c4d5734615a935760045460405260206040f35b63865147388118612c4d5760a436103417615a93576064358060a01c615a9357610600526084358060011c615a9357610620525b5f5c600114615a935760015f5d600435602435808201828110615a93579050905061048e576020806106a0526008610640527f21616d6f756e747300000000000000000000000000000000000000000000000061066052610640816106a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610680528060040161069cfd5b6011546106405260125461066052604036610680376011546106c0526012546106e0525f6002905b8061070052610700516002811015615a935760051b600401351561055b5761070051604052610700516002811015615a935760051b60040135606052336080525f60a052610505610720612c51565b61072051610700516002811015615a935760051b6106800152610700516002811015615a935760051b6106c0018051610700516002811015615a935760051b6106800151808201828110615a9357905090508152505b6001018181186104b65750506002546107005260406106c060405e6107005160805261058861076061307d565b6107606040816107205e50604061064060405e610700516080526105ad6107a061307d565b6107a06040816107605e506013546105c457426009555b6105cf6107e0612f24565b6107e06040816107a05e5060406107a060405e604061076060805e6105f5610800614527565b610800516107e0525f5463e68647666108205260406107a06108405e60406107206108805e5f6108c052602061082060a461083c845afa610638573d5f5f3e3d5ffd5b60203d10615a935761082090505161080052601d54610820525f610840526107e051156106a7576108205161080051808202811583838304141715615a9357905090506107e0518015615a93578082049050905061082051808203828111615a935790509050610840526106c9565b61080051604052610700516060526106c06108606133dd565b61086051610840525b61084051610749576020806108c052600e610860527f6e6f7468696e67206d696e74656400000000000000000000000000000000000061088052610860816108c001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108a052806004016108bcfd5b5f610860526107e05115610b3c5760406106806101605e60406107206101a05e610620516101e0526001610200525f6102205261078761088061458c565b6108805161084051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050610860526108405161086051808203828111615a93579050905061084052610620516109045761084051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a93578082049050905061088052610880511561083c57600a54151561083e565b5f5b156108e257600f546108a052610880516108a051808202811583838304141715615a9357905090506010548015615a9357808204905090506108a051808281188284100218905090506108c052600e5442808281188284110218905090506108e0526108e0516108c051808201828110615a935790509050426108a051808201828110615a935790509050808281188284100218905090506109005261090051600e555b61060051604052610840516060526108fb6108a061485b565b6108a050610b0a565b6106005115610985576020806108e0526010610880527f6e6f6e7a65726f207265636569766572000000000000000000000000000000006108a052610880816108e001603082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b600a5461084051808201828110615a93579050905061088052600b5461088051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a9357808204905090501115610a64576020806109005260136108a0527f646f6e6174696f6e2061626f76652063617021000000000000000000000000006108c0526108a08161090001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108e052806004016108fcfd5b5f604052610a736108c06132a7565b6108c051600c54808202811583838304141715615a935790509050610880518015615a9357808204905090506108a052426108a051808203828111615a935790509050600d5561088051600a55601d5461084051808201828110615a935790509050601d55337fc05458c16b884817a70d3d18223db5fe4adb4cb541a5573bef0daae7a6f2054260406106806108c05e60406108c0a25b60406107a06101005e60406107206101405e6108005161018052610b2f610880613517565b6108805161070052610b85565b61080051601355670de0b6b3a7640000601655670de0b6b3a7640000601455670de0b6b3a76400006015556106005160405261084051606052610b8061088061485b565b610880505b604435610840511015610c0a576020806108e0526008610880527f736c6970706167650000000000000000000000000000000000000000000000006108a052610880816108e001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b61060051337f0e1f3c59f25a027e14a3f55c68245d22089c42b1dcd09f123a11d4af3c0d6f7260406106806108805e610860516108c0526108205161084051808201828110615a9357905090506108e052610700516109005260a0610880a360206108405f5f5df35b635b36389c8118610c9257606436103417615a9357336102a052610cdd565b6392526c0c8118612c4d5734615a93576020601854604052610cb460606130f4565b6060f35b633eb1719f8118610f4657608436103417615a93576064358060a01c615a93576102a0525b5f5c600114615a935760015f5d601d546102c05233604052600435606052610d066102e06148cd565b6102e0506040366102e037601354610320526102c05160043518610d65575f6002905b8061034052610340516002811015615a935760110154610340516002811015615a935760051b6102e00152600101818118610d29575050610e71565b5f6002905b8061034052610340516002811015615a935760110154600435808202811583838304141715615a9357905090506102c0518015615a935780820490509050610340516002811015615a935760051b6102e00152610340516002811015615a935760051b60240135610340516002811015615a935760051b6102e001511015610e64576020806103c0526008610360527f736c69707061676500000000000000000000000000000000000000000000000061038052610360816103c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103a052806004016103bcfd5b600101818118610d6a5750505b610320516102c05161032051600435808202811583838304141715615a93579050905004808203828111615a9357905090506013555f6002905b806103405261034051604052610340516002811015615a935760051b6102e001516060526102a051608052610ede6143f8565b600101818118610eab575050337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102e06103405e6102c051600435808203828111615a935790509050610380526060610340a2610f3c61493f565b60406102e05f5f5df35b637c71109f8118612c4d5734615a9357600d5460405260206040f35b63512d63658118610f8157608436103417615a935733610a2052611072565b6323b872dd8118612c4d57606436103417615a93576004358060a01c615a9357610180526024358060a01c615a93576101a052601c610180516020525f5260405f2080336020525f5260405f209050546101c0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101c0511461102a5761018051604052336060526101c051604435808203828111615a93579050905060805261102a6156d3565b604061018060405e604435608052611040615728565b60016101e05260206101e0f35b63b2f9173e8118612c4d5760a436103417615a93576084358060a01c615a9357610a20525b5f5c600114615a935760015f5d60206080600461076037610a20516107e05261109c610a406153fd565b610a405f5f5df35b63f1dc3cc981186110c357606436103417615a935733610a2052611116565b634903b0d18118612c4d57602436103417615a93576004356002811015615a93576011015460405260206040f35b630fbcee6e8118612c4d57608436103417615a93576064358060a01c615a9357610a20525b5f5c600114615a935760015f5d6020600435610760526024358060010360018111615a93579050610780525f6107a0526044356107c052610a20516107e052611160610a406153fd565b610a405f5f5df35b631a2430cc8118612c4d5734615a9357601d54600a54808203828111615a93579050905060405260206040f35b634fb08c5e81186111fc57604436103417615a935760206111b7610760612f24565b6107606040816108405e50600435610880526024358060010360018111615a935790506108a0525f6108c05260a06108406103805e6111f76107a0614e6a565b6107a0f35b63244c7c2e8118612c4d5734615a93576112146159af565b61121f6101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a0526101a0516006556101a05160085542600755426009557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc28460406101606101c05e426102005260606101c0a1005b63a9059cbb81186112d957604436103417615a93576004358060a01c615a93576101805233604052610180516060526024356080526112cc615728565b60016101a05260206101a0f35b6386fc88d381186113045734615a93575f5c600114615a935760206112ff610160615833565b610160f35b630b7b594b8118612c4d5734615a935760155460405260206040f35b63095ea7b38118612c4d57604436103417615a93576004358060a01c615a935760c0523360405260c05160605260243560805261135b6156d3565b600160e052602060e0f35b63cab4d3db81186113cb5734615a935760206020615ba75f395f5163cab4d3db604052602060406004605c845afa6113a0573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b632a3f192b8118612c4d5734615a935760015460405260206040f35b63f851a4408118612c4d5734615a935760206020615ba75f395f5163f851a440604052602060406004605c845afa611421573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b63ed8e84f381186114b257606436103417615a93576044358060011c615a9357604052602060015463bc5bc6b76060526040600460803760405160c0523060e052602060606084607c845afa6114a4573d5f5f3e3d5ffd5b60203d10615a935760609050f35b6306fdde038118612c4d5734615a9357602080604052806040016060615bc782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63556d6e9f811861154e57606436103417615a93576020600154633bb1f8c1604052606060046060373060c052602060406084605c845afa611540573d5f5f3e3d5ffd5b60203d10615a935760409050f35b63c6610657811861158457602436103417615a935760206004356002811015615a935760051b604001615b270160403960206040f35b63e89876ff8118612c4d5734615a935760075460405260206040f35b6337ed3a7a81186115bf57606436103417615a935760056040526102c3565b630c46b72a8118612c4d5734615a935760165460405260206040f35b6354f0f7d581186117405734615a93575f5c600114615a93576016548060011b818160011c18615a93579050611612610160615833565b61016051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061165d578160801c91508060401b90505b6901000000000000000000821061167b578160401c91508060201b90505b650100000000008210611695578160201c91508060101b90505b630100000082106116ad578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050808202811583838304141715615a935790509050670de0b6b3a764000081049050610180526020610180f35b63b9e8c9fd8118612c4d5734615a93575f5c600114615a935760025460405260206040f35b63bb7b8b8081186117ce5734615a93575f5c600114615a935760135460405260025460605261179460806133dd565b608051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050601d548015615a93578082049050905060a052602060a0f35b63f446c1d081186117f05734615a935760206117eb610120612f24565b610120f35b634d23bfa08118612c4d5734615a935760055460405260206040f35b63ddca3f438118612c4d5734615a9357602060115460405260125460605260025460805261183b61016061307d565b6101606040816101c05e5060406101c060605e6118596101a0613130565b6101a0f35b63bcc8342e811861187f57608436103417615a935760403661038037611923565b63fe192e9e8118612c4d5734615a935760105460405260206040f35b63326cc61781186118c95760a436103417615a93576084358060011c615a9357610380525f6103a052611923565b6349fe9e778118612c4d5734615a935760206017546040526118eb60606130f4565b6060f35b6357832be68118612c4d5760c436103417615a93576084358060011c615a93576103805260a4358060011c615a93576103a0525b60206040600461016037604060446101a03760406103806101e05e60016102205261194f6103c061458c565b6103c0f35b63b13739298118612c4d5734615a93576020611971610120612f24565b610120602081019050f35b63ee8de6758118612c4d5734615a9357602060185460405261199e60606130f4565b6060602081019050f35b6372d4f0e281186119d45734615a935760206018546040526119ca60606130f4565b6060604081019050f35b6309c3da6a8118611a1f5734615a93576017546040526119f460606130f4565b6060604081019050516102b68102816102b6820418615a935790506103e88104905060c052602060c0f35b630f529ba28118612c4d5734615a935760135460405260206040f35b633620604b8118612c4d5734615a93576040615b2760403960406040f35b6380823d9e8118611a8757604436103417615a9357602060406004606037611a82610160613130565b610160f35b63dd62ed3e8118612c4d57604436103417615a93576004358060a01c615a93576040526024358060a01c615a9357606052601c6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b635e248072811861218757606436103417615a9357611afc6159af565b611b076101606130e8565b6101605115611b88576020806101e0526005610180527f2172616d700000000000000000000000000000000000000000000000000000006101a052610180816101e001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b42620151808101818110615a9357905060018103818111615a9357905060443511611c25576020806101c052600d610160527f72616d702074696d653c6d696e0000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b611c306101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a052614e206004351015611cd7576020806102205260056101c0527f413c6d696e0000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6305f5e1006004351115611d5d576020806102205260056101c0527f413e6d61780000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6402540be4006024351015611de4576020806102205260096101c0527f67616d6d613c6d696e00000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6702c2fd72164d80006024351115611e6e576020806102205260096101c0527f67616d6d653e6d617800000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b600435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610160518015615a9357808204905090506101c052678ac7230489e800006101c0511115611f2f5760208061024052600a6101e0527f4120746f6f206869676800000000000000000000000000000000000000000000610200526101e08161024001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c0511015611fba576020806102405260096101e0527f4120746f6f206c6f770000000000000000000000000000000000000000000000610200526101e08161024001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b602435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610180518015615a9357808204905090506101c052678ac7230489e800006101c051111561207b5760208061024052600e6101e0527f67616d6d6120746f6f2068696768000000000000000000000000000000000000610200526101e08161024001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c05110156121065760208061024052600d6101e0527f67616d6d6120746f6f206c6f7700000000000000000000000000000000000000610200526101e08161024001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b6101a0516006554260075560043560801b6101e0526024356101e051176101e0526044356009556101e0516008557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd0561016051610200526004356102205261018051610240526024356102605242610280526044356102a05260c0610200a1005b63d046b4ca8118612c4d5734615a9357600e5460405260206040f35b636dbcf350811861253d5760c436103417615a93575f5c600114615a935760015f5d6121cd6159af565b60606004610160376018546040526121e66102206130f4565b6102206060816101c05e506402540be40061018051111561220e576101e05161018052612294565b6207a12061018051101561229457602080610280526004610220527f2166656500000000000000000000000000000000000000000000000000000000610240526102208161028001602482825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b6402540be40161016051106122ac576101c051610160525b6101805161016051111561233257602080610280526008610220527f216d69642d666565000000000000000000000000000000000000000000000000610240526102208161028001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b670de0b6b3a763ffff6101a051111561235257610200516101a0526123d2565b6101a0516123d25760208061028052600a610220527f216665655f67616d6d6100000000000000000000000000000000000000000000610240526102208161028001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b606061016060405e6123e5610220615a7d565b6102205160185560606064610220376017546040526124056102e06130f4565b6102e06060816102805e50670de0b6b3a7640001610220511061242b5761028051610220525b670de0b6b3a76400016102405110612446576102a051610240525b620d505d610260511115612461576102c051610260526124e5565b60576102605110156124e55760208061034052600b6102e0527f4d413c36302f6c6e283229000000000000000000000000000000000000000000610300526102e08161034001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610320528060040161033cfd5b606061022060405e6124f86102e0615a7d565b6102e0516017557fa32137411fc7c20db359079cd84af0e2cad58cd7a182a8a5e23e08e554e88bf060606101606102e05e60606102206103405e60c06102e0a15f5f5d005b6354fd4d508118612c4d5734615a935760208060805260076040527f76322e312e306400000000000000000000000000000000000000000000000000606052604081608001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b63f5b2f0168118612c4d57602436103417615a93576125cf6159af565b60043561264e576020806101c0526009610160527f216475726174696f6e000000000000000000000000000000000000000000000061018052610160816101c001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600c557fb2cf7972e8e7c2db8a62b4c568cf133a24bf5910b2603ad8811e6bfc9a865322600435610160526020610160a1005b638325c0028118612c4d57606436103417615a93576126a16159af565b600435612720576020806101c0526007610160527f21706572696f640000000000000000000000000000000000000000000000000061018052610160816101c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60243561279f576020806101c052600a610160527f217468726573686f6c640000000000000000000000000000000000000000000061018052610160816101c001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60443561281e576020806101c052600b610160527f216d61785f73686172657300000000000000000000000000000000000000000061018052610160816101c001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600f55602435601055604435600b557f6db4ade9cd8d6e671d6d713ab38d8889f9e3d4bbb319ca3389a516cf4efcf19d60606004610160376060610160a1005b633217902f8118612c4d57602436103417615a935761287e6159af565b6402540be4006004351115612905576020806101c052600d610160527f61646d696e5f6665653e4d41580000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b6004356019557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b74600435610160526020610160a1005b636fe26a348118612c4d57604436103417615a93576004358060a01c615a9357610160526024358060a01c615a9357610180526129766159af565b610160511561298657600161298d565b6101805115155b612a09576020806102005260096101a0527f21636f6e747261637400000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6101605115612a1a57610160516001555b6101805115612a2a57610180515f555b7f567da2faedc99f951794c7dc379b4906dcdd1d016fedb16916d1e9623901f21660406101606101a05e60406101a0a1005b63ed6c15468118612c4d5734615a93575f5460405260206040f35b63c45a01558118612a955734615a93576020615ba760403960206040f35b637ba1a74d8118612ab15734615a935760145460405260206040f35b63e36164058118612c4d5734615a935760185460405260206040f35b63204fe3d58118612c4d5734615a935760065460405260206040f35b63f30cfad58118612b055734615a935760085460405260206040f35b6318160ddd8118612c4d5734615a9357601d5460405260206040f35b63a3bdf1b78118612c4d5734615a9357600a5460405260206040f35b633d2699f28118612c4d5734615a9357600b5460405260206040f35b630decf4a28118612c4d5734615a9357600c5460405260206040f35b631f88619c8118612c4d5734615a9357600f5460405260206040f35b63fee3f7f98118612c4d5734615a935760195460405260206040f35b6395d89b418118612c4d5734615a9357602080604052806040016040615c2782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63313ce5678118612c4d5734615a9357601260405260206040f35b6370a082318118612c4d57602436103417615a93576004358060a01c615a9357604052601b6040516020525f5260405f205460605260206060f35b5f5ffd5b60206040516002811015615a935760051b604001615b27015f395f516370a0823160e0523061010052602060e0602460fc845afa612c91573d5f5f3e3d5ffd5b60203d10615a935760e090505160c05260a05115612d815760c0516040516002811015615a935760110154808203828111615a93579050905060e05260605160e0511015612d5157602080610160526006610100527f21636f696e730000000000000000000000000000000000000000000000000000610120526101008161016001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610140528060040161015cfd5b6040516002811015615a9357601101805460e051808201828110615a93579050905081555060e051815250612f22565b60206040516002811015615a935760051b604001615b27015f395f516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc5f855af1612dd0573d5f5f3e3d5ffd5b3d612de757803b15615a9357600161016052612e0f565b3d602081183d60201002188060e00161010011615a935760e0518060011c615a935761016052505b610160905051612e91576020806101e052600d610180527f217472616e7366657246726f6d000000000000000000000000000000000000006101a052610180816101e001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b60206040516002811015615a935760051b604001615b27015f395f516370a082316101005230610120526020610100602461011c845afa612ed4573d5f5f3e3d5ffd5b60203d10615a935761010090505160c051808203828111615a93579050905060e0526040516002811015615a9357601101805460e051808201828110615a93579050905081555060e0518152505b565b6009546040526008546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a05260405142101561306d5760065460c05260075460e05260405160e051808203828111615a9357905090506040524260e051808203828111615a93579050905060e05260405160e051808203828111615a9357905090506101005260c05160801c61010051808202811583838304141715615a93579050905060a05160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a93578082049050905060a0526fffffffffffffffffffffffffffffffff60c0511661010051808202811583838304141715615a93579050905060805160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a9357808204905090506080525b60a0518152608051602082015250565b6040516020615b275f395f51808202811583838304141715615a9357905090508152670de0b6b3a76400006060516020615b475f395f51808202811583838304141715615a935790509050608051808202811583838304141715615a93579050905004602082015250565b60055460095411815250565b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b6018546040526131416101006130f4565b61010060608160a05e50606051608051808201828110615a93579050905061010052606051673782dace9d900000810281673782dace9d900000820418615a93579050610100518015615a935780820490509050608051808202811583838304141715615a935790509050610100518015615a9357808204905090506101005260e05161010051808202811583838304141715615a935790509050670de0b6b3a764000060e05161010051808202811583838304141715615a93579050905004670de0b6b3a76400008101818110615a9357905061010051808203828111615a9357905090508015615a93578082049050905061010052670de0b6b3a764000060a05161010051808202811583838304141715615a93579050905060c0516101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050808201828110615a93579050905004815250565b600a546060526060516132bd575f8152506133db565b42600d54808203828111615a935790509050608052606051606051608051808202811583838304141715615a935790509050600c548015615a9357808204905090508082811882841002189050905060a0526040516133215760a0518152506133db565b5f60c052600e5460e0524260e05111156133945760e05142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905060c0525b60a05160c05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050670de0b6b3a7640000810490508152505b565b604051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060011c9050606051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061344e578160801c91508060401b90505b6901000000000000000000821061346c578160401c91508060201b90505b650100000000008210613486578160201c91508060101b90505b6301000000821061349e578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090508015615a935780820490509050815250565b6003546101a0526004546101c0526002546101e05260175460405261353d6102606130f4565b6102606060816102005e506135536102806130e8565b6102805161026052600554610280525f6102a052426102805110156136a3575f546381d18d876102c05261024051610280514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050048060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f036102e05260206102c060246102dc845afa6135f4573d5f5f3e3d5ffd5b60203d10615a93576102c09050516102a052670de0b6b3a76400006101c0516101e0518060011b818160011c18615a93579050808281188284100218905090506102a05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a9357905090506101a0516102a051808202811583838304141715615a935790509050808201828110615a935790509050046101a0526101a051600355426005555b670de0b6b3a76400005f54637e0e395e6102c05260406101406102e05e610180516103205260406101006103405e60206102c060a46102dc845afa6136ea573d5f5f3e3d5ffd5b60203d10615a93576102c09050516101e051808202811583838304141715615a93579050905004600455601d546102c052600160405261372b6103006132a7565b610300516102e0526102c0516102e051808203828111615a9357905090506103005260165461032052610180516040526101e05160605261376d6103606133dd565b610360516103405261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a935780820490509050610360526103205161036051101561383a576102605161383a576020806103e0526017610380527f7669727475616c207072696365206465637265617365640000000000000000006103a052610380816103e001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103c052806004016103dcfd5b60145461036051808201828110615a93579050905061032051808203828111615a935790509050610380526103805160145561038051670de0b6b3a76400008101818110615a935790508060011c905080670de0b6b3a7640000811882670de0b6b3a764000011021890506103a05261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610300518015615a9357808204905090506103c052610360516103c0511015613966576020806104405260116103e0527f6e6567617469766520646f6e6174696f6e000000000000000000000000000000610400526103e08161044001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610420528060040161043cfd5b6103a05161020051808201828110615a9357905090506103c0511161398b575f613992565b4261028051105b15613de0576101e051670de0b6b3a76400006101a05102046103e052670de0b6b3a76400016103e05110156139d8576103e051670de0b6b3a7640000036103e0526139eb565b670de0b6b3a76400006103e051036103e0525b6102205160056103e051048082811882841102189050905061040052610400516103e0511115613de0576103e0516101e051610400516103e05103808202811583838304141715615a935790509050610400516101a051808202811583838304141715615a935790509050808201828110615a935790509050046104205261014051610440526101e0516101605161042051808202811583838304141715615a93579050905004610460525f5463e68647666104a05260406101006104c05e60406104406105005e5f6105405260206104a060a46104bc845afa613ad1573d5f5f3e3d5ffd5b60203d10615a93576104a0905051610480526104805160405261042051606052613afc6104c06133dd565b6104c0516104a0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a9357808204905090506104c0525f6104e0526103a051610360518082811882841102189050905061050052610500516104c0511015613c8b576104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610500518015615a935780820490509050610520526102c0516105205110613c23576020806105a052601a610540527f747765616b656420737570706c79206d75737420736872696e6b00000000000061056052610540816105a001603a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610580528060040161059cfd5b610520516102c051036102e051808281188284100218905090506104e0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0516104e051808203828111615a9357905090508015615a9357808204905090506104c0525b670de0b6b3a76400016104c0511015613ca4575f613caf565b6103a0516104c05110155b15613de057610480516013556104c051601655610420516002556104e05115613dd4575f604052613ce16105406132a7565b61054051610520526102e05161054052610520516104e05161052051808202811583838304141715615a935790509050610540518015615a935780820490509050808203828111615a93579050905061056052600a546104e051808203828111615a935790509050610580525f6105a0526105805115613d6657610560511515613d68565b5f5b15613d9e5761056051600c54808202811583838304141715615a935790509050610580518015615a9357808204905090506105a0525b61058051600a55601d546104e051808203828111615a935790509050601d55426105a051808203828111615a935790509050600d555b61042051815250613df6565b61018051601355610360516016556101e0518152505b565b610620516106005118613e7d576020806106e0526009610680527f73616d6520636f696e00000000000000000000000000000000000000000000006106a052610680816106e001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b61064051613efd576020806106e0526007610680527f7a65726f206478000000000000000000000000000000000000000000000000006106a052610680816106e001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b613f086106c0612f24565b6106c06040816106805e506011546106c0526012546106e0525f61070052610620516002811015615a935760051b6106c0015161072052610600516002811015615a935760051b6106c0015161064051808203828111615a935790509050610740526002546107605260406106c060405e61076051608052613f8b6107c061307d565b6107c06040816107805e50613fa16107c06130e8565b6107c051156140ac57610740516020610600516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061074052610600511561401457670de0b6b3a76400006107405161076051808202811583838304141715615a93579050905004610740525b610600516002811015615a935760051b61078001516107e05261074051610600516002811015615a935760051b61078001525f5463e68647666108005260406106806108205e60406107806108605e5f6108a052602061080060a461081c845afa614081573d5f5f3e3d5ffd5b60203d10615a93576108009050516013556107e051610600516002811015615a935760051b61078001525b6013546107c0525f546343d188fb6108205260406106806108405e60406107806108805e6107c0516108c052610620516108e052604061082060c461083c845afa6140f9573d5f5f3e3d5ffd5b60403d10615a935761082090506040816107e05e50610620516002811015615a935760051b61078001516107e051808203828111615a93579050905061070052610620516002811015615a935760051b61078001805161070051808203828111615a9357905090508152506107005160018103818111615a935790506107005261062051156141ba5761070051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610760518015615a935780820490509050610700525b610700516020610620516002811015615a935760051b615b27015f395f518015615a935780820490509050610700526402540be400604061078060605e614202610840613130565b6108405161070051808202811583838304141715615a93579050905004610820526107005161082051808203828111615a93579050905061070052610660516107005110156142c3576020806108a0526008610840527f736c69707061676500000000000000000000000000000000000000000000000061086052610840816108a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610880528060040161089cfd5b6107205161070051808203828111615a93579050905061072052610720516020610620516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061072052610620511561434757670de0b6b3a76400006107205161076051808202811583838304141715615a93579050905004610720525b61072051610620516002811015615a935760051b61078001525f5463e68647666108405260406106806108605e60406107806108a05e610800516108e052602061084060a461085c845afa61439e573d5f5f3e3d5ffd5b60203d10615a93576108409050516107c05260406106806101005e60406107806101405e6107c051610180526143d5610840613517565b610840516107605261070051815261082051602082015261076051604082015250565b6040516002811015615a93576011018054606051808203828111615a93579050905081555060206040516002811015615a935760051b604001615b27015f395f5163a9059cbb60a05260805160c05260605160e052602060a0604460bc5f855af1614465573d5f5f3e3d5ffd5b3d61447c57803b15615a93576001610100526144a3565b3d602081183d60201002188060a00160c011615a935760a0518060011c615a935761010052505b61010090505161452557602080610180526009610120527f217472616e736665720000000000000000000000000000000000000000000000610140526101208161018001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610160528060040161017cfd5b565b61453160c06130e8565b60c0516145435760135481525061458a565b5f5463e686476660e052604060406101005e604060806101405e5f61018052602060e060a460fc845afa614579573d5f5f3e3d5ffd5b60203d10615a935760e09050518152505b565b6101e051156145a157620186a0815250614859565b60406101606102405e61022051156145bb57604036610240375b60115461024051808203828111615a9357905090506020615b275f395f51808202811583838304141715615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060125461026051808203828111615a9357905090506020615b475f395f51808202811583838304141715615a9357905090508015615a93578082049050905061028052604061016060405e610280516080526146686102a061307d565b6102a06040816102e05e5060406102e06101605e60406101a060605e61468f6102c0613130565b6102c05160011b60021c6102a0525f6102c0525f6002905b8060051b61016001516102e0526102c0516102e051808201828110615a9357905090506102c0526001018181186146a75750506102c05160011c6102e0525f610300525f6002905b8060051b6101600151610320526102e051610320511161472d5761030051610320516102e05103808201828110615a9357905090506103005261474d565b610300516102e0516103205103808201828110615a935790509050610300525b6001018181186146ef5750505f61032052610200511561480757600e546103405242610340511115614807576103405142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905061036052610360516102a051808202811583838304141715615a935790509050670de0b6b3a764000081049050610320525b6102a05161030051808202811583838304141715615a9357905090506102c0518015615a935780820490509050620186a08101818110615a9357905061032051808201828110615a9357905090508152505b565b601d54606051808201828110615a935790509050601d55601b6040516020525f5260405f208054606051808201828110615a9357905090508155506040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54606051808203828111615a935790509050601d55601b6040516020525f5260405f208054606051808203828111615a9357905090508155505f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54600a541415614aac576020615ba75f395f5163cab4d3db610200526020610200600461021c845afa614976573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0526101e051614a10576020615ba75f395f5163f851a440610200526020610200600461021c845afa6149dc573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0525b60115461020052601254610220525f6002905b806102405261024051604052610240516002811015615a935760051b61020001516060526101e051608052614a566143f8565b600101818118614a235750505f600a555f601d555f6013555f600e556101e0517fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102006102405e5f610280526060610240a25b565b601a546101e0526201517f6101e05142031115614ad957614ad06102006130e8565b61020051614adc565b60015b614e68576014546102005260155461022052601d546102405261022051610200511115614b1757670de0b6b3a763ffff610240511115614b1a565b60015b614e685760135461026052601654610280526002546102a0526020615ba75f395f5163cab4d3db6102e05260206102e060046102fc845afa614b5e573d5f5f3e3d5ffd5b3d602081183d6020100218806102e00161030011615a93576102e0518060a01c615a935761032052506103209050516102c0526011546102e052601254610300526404a817c800610220516102005103601954808202811583838304141715615a93579050905004610320525f610340526102c05115614be357610320511515614be5565b5f5b15614cb15761028051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102805161032051808203828111615a9357905090508015615a935780820490509050670de0b6b3a76400008103818111615a9357905061036052610340516102405161036051808202811583838304141715615a935790509050670de0b6b3a764000081049050808201828110615a9357905090506103405261020051610320518060011b818160011c18615a93579050808203828111615a935790509050610200525b6102405161034051808201828110615a93579050905061036052610260516040526102a051606052614ce46103806133dd565b61038051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610360518015615a93578082049050905061028052670de0b6b3a763ffff610280511115614e68576102005160145542601a556102805160165561026051610360516102605161034051808202811583838304141715615a93579050905004808203828111615a93579050905060135561022051610200511115614d8c57610200516015555b604036610380376103405115614e68575f6002905b806103c0526103c0516002811015615a935760051b6102e0015161034051808202811583838304141715615a935790509050610360518015615a9357808204905090506103c0516002811015615a935760051b61038001526103c0516040526103c0516002811015615a935760051b61038001516060526102c051608052614e276143f8565b600101818118614da15750506102c0517f3bbd5f2f4711532d6e9ee88dfdf2f1468e9a4c3ae5e14d2e1a67bf4242d008d060406103806103c05e60406103c0a25b565b601d5461042052610420516103c0511115614ef7576020806104a0526007610440527f21616d6f756e740000000000000000000000000000000000000000000000000061046052610440816104a001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610480528060040161049cfd5b6103e0518060010360018111615a935790506104405260115461046052601254610480526002546104a052604061046060405e6104a051608052614f3c61050061307d565b6105006040816104c05e50604061038060405e60406104c060805e614f62610520614527565b6105205161050052610420516103c05161050051808202811583838304141715615a935790509050046105205260406104c06105405e6020615b275f395f51670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610580526104a0516020615b475f395f51808202811583838304141715615a9357905090506105a0526040366105c037670de0b6b3a7640000610400516103e0516002811015615a935760051b6105800151808202811583838304141715615a935790509050046103e0516002811015615a935760051b6105c001526103e0516002811015615a935760051b6105400180516103e0516002811015615a935760051b6105c00151808203828111615a9357905090508152505f546343d188fb6106205260406103806106405e60406105406106805e6105005161052051808203828111615a9357905090506106c052610440516106e052604061062060c461063c845afa6150cf573d5f5f3e3d5ffd5b60403d10615a935761062090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050610440516002811015615a935760051b6105c0015261060051610440516002811015615a935760051b610540015260403661062037610400516103e0516002811015615a935760051b61062001526103e0516151b0576105e051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506020615b475f395f518015615a9357808204905090506104a0518015615a935780820490509050610640526151cf565b6105c0516020615b275f395f518015615a935780820490509050610620525b6106205161064051808201828110615a935790509050615261576020806106c0526007610660527f21746f6b656e730000000000000000000000000000000000000000000000000061068052610660816106c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106a052806004016106bcfd5b60406106206101605e60406105406101a05e6060366101e03761528561068061458c565b6106805161066052610520516105205161066051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050808203828111615a935790509050610520525f546343d188fb6106805260406103806106a05e60406105406106e05e6105005161052051808203828111615a935790509050610720526104405161074052604061068060c461069c845afa61532e573d5f5f3e3d5ffd5b60403d10615a935761068090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610440516002811015615a935760051b61058001518015615a9357808204905090506106805261060051610440516002811015615a935760051b61054001526106805181526105005161052051808203828111615a9357905090506020820152604081016040610540825e5061066051608082015250565b615405614aae565b615410610840612f24565b6108406040816108005e5060a0366108403760406108006103805e60606107606103c05e61543f6108e0614e6a565b6108e060a0816109805e5060406109806108405e60406109c06108805e610a00516108c0526107c0516108405110156154ea576020806109405260086108e0527f736c697070616765000000000000000000000000000000000000000000000000610900526108e08161094001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610920528060040161093cfd5b33604052610760516060526155006108e06148cd565b6108e05060406108006101005e60406108806101405e6108605161018052615529610900613517565b610900516108e0526107a0511561555157604061078060405e6107e0516080526155516143f8565b610780518060010360018111615a935790506109005261090051604052610840516060526107e0516080526155846143f8565b604036610920376107a051610780516002811015615a935760051b610920015261084051610900516002811015615a935760051b61092001526107a05161564957337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c61076051610960526109005161098052610840516109a0526108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a26156c2565b337f22f9ea3e7d7b113cb423896d2e121f96a66c17814ac7f63d69096769fa3e2a55610760516109605260406109206109805e6108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a25b6156ca61493f565b61084051815250565b608051601c6040516020525f5260405f20806060516020525f5260405f209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b60605130811461573a5780151561573c565b5f5b90506157b75760208061012052600960c0527f217265636569766572000000000000000000000000000000000000000000000060e05260c08161012001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b601b6040516020525f5260405f208054608051808203828111615a935790509050815550601b6060516020525f5260405f208054608051808201828110615a9357905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b60035460605260025460805260055460a0524260a05110156159a65760045460c0526017546040526158666101006130f4565b6101006040810190505160e0525f546381d18d876101205260a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060e0518015615a9357808204905090508060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f03610140526020610120602461013c845afa6158ff573d5f5f3e3d5ffd5b60203d10615a93576101209050516101005260c0516080518060011b818160011c18615a93579050808281188284100218905090506101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a93579050905060605161010051808202811583838304141715615a935790509050808201828110615a935790509050670de0b6b3a7640000810490508152506159ad565b6060518152505b565b6020615ba75f395f5163f851a440604052602060406004605c845afa6159d7573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a9357608052506080905051331815615a7b5760208061010052600a60a0527f6f6e6c79206f776e65720000000000000000000000000000000000000000000060c05260a08161010001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b565b60805160605160401b60405160801b1717815250565b5f80fd2c4d2c122b3d2c4d2c4d2acd185e14fc00852c4d2c4d13200c7315db104d2861001810a41adf180c0373197c2a5c2b2103bd2c4d268401942c4d0f6211950cb813e72bad15a01a3b293b2ae918ef2bf717652c4d2c4d128f11682c4d2c4d2c4d2c4d2c4d2c4d2c4d2c4d03231a59189b21a319542b5913662b752c4d10f101b32c4d2b9119a8144c2c4d2a7725b22c4d855820bf0aeab4893a0fb1ddfeb10813812fb2348a0c919a25f46048ec7223d56f2c74195b27811890190140a1657679706572830004030039000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000001fd8af16dc4bebd950521308d55d0543b6cdf4a1d6fe432991d14bd469ee0cce1efbd32851eb4eff43b257126e94d293caf48361000000000000000000000002540be4000000000000000000000000000000000100000000000000000000000000015f90000000000000000000038d7ea4c6800000000000000000000000000005f5e1000000000005f5e100000aa87bee53800000000000000000000000000005f5e100000000174876e80000000000000003620000000000000000000000000000000000000000000017edbe05b457450000000000000000000000000000000000000000000000000000000000000000000007594220574254430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086379622d77627463000000000000000000000000000000000000000000000000
Deployed Bytecode
0x5f3560e01c60026048820660011b615a9701601e395f51565b635b41b908811861003757608436103417615a935733610900526100aa565b63d5f8da308118612c4d57606436103417615a93576020610059610760612f24565b6107606040816108405e50606060046108803760a06108406103805e6100806107a0614e6a565b6107a0f35b63a64833a081186101785760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d600435604052604435606052336080525f60a0526100d6610940612c51565b610940516109205260406004610600376109205161064052606435610660526101006109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526101276143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b633dd654788118612c4d5734615a935760175460405260206040f35b6329b244bb8118612c4d57608436103417615a935733610900526101d8565b63767691e781186102a75760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d60043560405260443560605233608052600160a052610205610940612c51565b6109405161092052604060046106003761092051610640526064356106605261022f6109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526102566143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b6348155d27811861030757608436103417615a93576064356040525b60206001546333e39968606052606060046080373060e052604051610100526020606060a4607c845afa6102f9573d5f5f3e3d5ffd5b60203d10615a935760609050f35b63f9ed95978118612c4d5734615a935760095460405260206040f35b630b4c7e4d811861034757606436103417615a935733610600525f610620526103f1565b63083812e58118612c4d5734615a9357602060175460405261036960606130f4565b6060602081019050f35b630c3e4b5481186103a157608436103417615a93576064358060a01c615a9357610600525f610620526103f1565b63c146bf948118612c4d5734615a935760045460405260206040f35b63865147388118612c4d5760a436103417615a93576064358060a01c615a9357610600526084358060011c615a9357610620525b5f5c600114615a935760015f5d600435602435808201828110615a93579050905061048e576020806106a0526008610640527f21616d6f756e747300000000000000000000000000000000000000000000000061066052610640816106a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610680528060040161069cfd5b6011546106405260125461066052604036610680376011546106c0526012546106e0525f6002905b8061070052610700516002811015615a935760051b600401351561055b5761070051604052610700516002811015615a935760051b60040135606052336080525f60a052610505610720612c51565b61072051610700516002811015615a935760051b6106800152610700516002811015615a935760051b6106c0018051610700516002811015615a935760051b6106800151808201828110615a9357905090508152505b6001018181186104b65750506002546107005260406106c060405e6107005160805261058861076061307d565b6107606040816107205e50604061064060405e610700516080526105ad6107a061307d565b6107a06040816107605e506013546105c457426009555b6105cf6107e0612f24565b6107e06040816107a05e5060406107a060405e604061076060805e6105f5610800614527565b610800516107e0525f5463e68647666108205260406107a06108405e60406107206108805e5f6108c052602061082060a461083c845afa610638573d5f5f3e3d5ffd5b60203d10615a935761082090505161080052601d54610820525f610840526107e051156106a7576108205161080051808202811583838304141715615a9357905090506107e0518015615a93578082049050905061082051808203828111615a935790509050610840526106c9565b61080051604052610700516060526106c06108606133dd565b61086051610840525b61084051610749576020806108c052600e610860527f6e6f7468696e67206d696e74656400000000000000000000000000000000000061088052610860816108c001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108a052806004016108bcfd5b5f610860526107e05115610b3c5760406106806101605e60406107206101a05e610620516101e0526001610200525f6102205261078761088061458c565b6108805161084051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050610860526108405161086051808203828111615a93579050905061084052610620516109045761084051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a93578082049050905061088052610880511561083c57600a54151561083e565b5f5b156108e257600f546108a052610880516108a051808202811583838304141715615a9357905090506010548015615a9357808204905090506108a051808281188284100218905090506108c052600e5442808281188284110218905090506108e0526108e0516108c051808201828110615a935790509050426108a051808201828110615a935790509050808281188284100218905090506109005261090051600e555b61060051604052610840516060526108fb6108a061485b565b6108a050610b0a565b6106005115610985576020806108e0526010610880527f6e6f6e7a65726f207265636569766572000000000000000000000000000000006108a052610880816108e001603082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b600a5461084051808201828110615a93579050905061088052600b5461088051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a9357808204905090501115610a64576020806109005260136108a0527f646f6e6174696f6e2061626f76652063617021000000000000000000000000006108c0526108a08161090001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108e052806004016108fcfd5b5f604052610a736108c06132a7565b6108c051600c54808202811583838304141715615a935790509050610880518015615a9357808204905090506108a052426108a051808203828111615a935790509050600d5561088051600a55601d5461084051808201828110615a935790509050601d55337fc05458c16b884817a70d3d18223db5fe4adb4cb541a5573bef0daae7a6f2054260406106806108c05e60406108c0a25b60406107a06101005e60406107206101405e6108005161018052610b2f610880613517565b6108805161070052610b85565b61080051601355670de0b6b3a7640000601655670de0b6b3a7640000601455670de0b6b3a76400006015556106005160405261084051606052610b8061088061485b565b610880505b604435610840511015610c0a576020806108e0526008610880527f736c6970706167650000000000000000000000000000000000000000000000006108a052610880816108e001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b61060051337f0e1f3c59f25a027e14a3f55c68245d22089c42b1dcd09f123a11d4af3c0d6f7260406106806108805e610860516108c0526108205161084051808201828110615a9357905090506108e052610700516109005260a0610880a360206108405f5f5df35b635b36389c8118610c9257606436103417615a9357336102a052610cdd565b6392526c0c8118612c4d5734615a93576020601854604052610cb460606130f4565b6060f35b633eb1719f8118610f4657608436103417615a93576064358060a01c615a93576102a0525b5f5c600114615a935760015f5d601d546102c05233604052600435606052610d066102e06148cd565b6102e0506040366102e037601354610320526102c05160043518610d65575f6002905b8061034052610340516002811015615a935760110154610340516002811015615a935760051b6102e00152600101818118610d29575050610e71565b5f6002905b8061034052610340516002811015615a935760110154600435808202811583838304141715615a9357905090506102c0518015615a935780820490509050610340516002811015615a935760051b6102e00152610340516002811015615a935760051b60240135610340516002811015615a935760051b6102e001511015610e64576020806103c0526008610360527f736c69707061676500000000000000000000000000000000000000000000000061038052610360816103c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103a052806004016103bcfd5b600101818118610d6a5750505b610320516102c05161032051600435808202811583838304141715615a93579050905004808203828111615a9357905090506013555f6002905b806103405261034051604052610340516002811015615a935760051b6102e001516060526102a051608052610ede6143f8565b600101818118610eab575050337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102e06103405e6102c051600435808203828111615a935790509050610380526060610340a2610f3c61493f565b60406102e05f5f5df35b637c71109f8118612c4d5734615a9357600d5460405260206040f35b63512d63658118610f8157608436103417615a935733610a2052611072565b6323b872dd8118612c4d57606436103417615a93576004358060a01c615a9357610180526024358060a01c615a93576101a052601c610180516020525f5260405f2080336020525f5260405f209050546101c0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101c0511461102a5761018051604052336060526101c051604435808203828111615a93579050905060805261102a6156d3565b604061018060405e604435608052611040615728565b60016101e05260206101e0f35b63b2f9173e8118612c4d5760a436103417615a93576084358060a01c615a9357610a20525b5f5c600114615a935760015f5d60206080600461076037610a20516107e05261109c610a406153fd565b610a405f5f5df35b63f1dc3cc981186110c357606436103417615a935733610a2052611116565b634903b0d18118612c4d57602436103417615a93576004356002811015615a93576011015460405260206040f35b630fbcee6e8118612c4d57608436103417615a93576064358060a01c615a9357610a20525b5f5c600114615a935760015f5d6020600435610760526024358060010360018111615a93579050610780525f6107a0526044356107c052610a20516107e052611160610a406153fd565b610a405f5f5df35b631a2430cc8118612c4d5734615a9357601d54600a54808203828111615a93579050905060405260206040f35b634fb08c5e81186111fc57604436103417615a935760206111b7610760612f24565b6107606040816108405e50600435610880526024358060010360018111615a935790506108a0525f6108c05260a06108406103805e6111f76107a0614e6a565b6107a0f35b63244c7c2e8118612c4d5734615a93576112146159af565b61121f6101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a0526101a0516006556101a05160085542600755426009557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc28460406101606101c05e426102005260606101c0a1005b63a9059cbb81186112d957604436103417615a93576004358060a01c615a93576101805233604052610180516060526024356080526112cc615728565b60016101a05260206101a0f35b6386fc88d381186113045734615a93575f5c600114615a935760206112ff610160615833565b610160f35b630b7b594b8118612c4d5734615a935760155460405260206040f35b63095ea7b38118612c4d57604436103417615a93576004358060a01c615a935760c0523360405260c05160605260243560805261135b6156d3565b600160e052602060e0f35b63cab4d3db81186113cb5734615a935760206020615ba75f395f5163cab4d3db604052602060406004605c845afa6113a0573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b632a3f192b8118612c4d5734615a935760015460405260206040f35b63f851a4408118612c4d5734615a935760206020615ba75f395f5163f851a440604052602060406004605c845afa611421573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b63ed8e84f381186114b257606436103417615a93576044358060011c615a9357604052602060015463bc5bc6b76060526040600460803760405160c0523060e052602060606084607c845afa6114a4573d5f5f3e3d5ffd5b60203d10615a935760609050f35b6306fdde038118612c4d5734615a9357602080604052806040016060615bc782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63556d6e9f811861154e57606436103417615a93576020600154633bb1f8c1604052606060046060373060c052602060406084605c845afa611540573d5f5f3e3d5ffd5b60203d10615a935760409050f35b63c6610657811861158457602436103417615a935760206004356002811015615a935760051b604001615b270160403960206040f35b63e89876ff8118612c4d5734615a935760075460405260206040f35b6337ed3a7a81186115bf57606436103417615a935760056040526102c3565b630c46b72a8118612c4d5734615a935760165460405260206040f35b6354f0f7d581186117405734615a93575f5c600114615a93576016548060011b818160011c18615a93579050611612610160615833565b61016051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061165d578160801c91508060401b90505b6901000000000000000000821061167b578160401c91508060201b90505b650100000000008210611695578160201c91508060101b90505b630100000082106116ad578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050808202811583838304141715615a935790509050670de0b6b3a764000081049050610180526020610180f35b63b9e8c9fd8118612c4d5734615a93575f5c600114615a935760025460405260206040f35b63bb7b8b8081186117ce5734615a93575f5c600114615a935760135460405260025460605261179460806133dd565b608051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050601d548015615a93578082049050905060a052602060a0f35b63f446c1d081186117f05734615a935760206117eb610120612f24565b610120f35b634d23bfa08118612c4d5734615a935760055460405260206040f35b63ddca3f438118612c4d5734615a9357602060115460405260125460605260025460805261183b61016061307d565b6101606040816101c05e5060406101c060605e6118596101a0613130565b6101a0f35b63bcc8342e811861187f57608436103417615a935760403661038037611923565b63fe192e9e8118612c4d5734615a935760105460405260206040f35b63326cc61781186118c95760a436103417615a93576084358060011c615a9357610380525f6103a052611923565b6349fe9e778118612c4d5734615a935760206017546040526118eb60606130f4565b6060f35b6357832be68118612c4d5760c436103417615a93576084358060011c615a93576103805260a4358060011c615a93576103a0525b60206040600461016037604060446101a03760406103806101e05e60016102205261194f6103c061458c565b6103c0f35b63b13739298118612c4d5734615a93576020611971610120612f24565b610120602081019050f35b63ee8de6758118612c4d5734615a9357602060185460405261199e60606130f4565b6060602081019050f35b6372d4f0e281186119d45734615a935760206018546040526119ca60606130f4565b6060604081019050f35b6309c3da6a8118611a1f5734615a93576017546040526119f460606130f4565b6060604081019050516102b68102816102b6820418615a935790506103e88104905060c052602060c0f35b630f529ba28118612c4d5734615a935760135460405260206040f35b633620604b8118612c4d5734615a93576040615b2760403960406040f35b6380823d9e8118611a8757604436103417615a9357602060406004606037611a82610160613130565b610160f35b63dd62ed3e8118612c4d57604436103417615a93576004358060a01c615a93576040526024358060a01c615a9357606052601c6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b635e248072811861218757606436103417615a9357611afc6159af565b611b076101606130e8565b6101605115611b88576020806101e0526005610180527f2172616d700000000000000000000000000000000000000000000000000000006101a052610180816101e001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b42620151808101818110615a9357905060018103818111615a9357905060443511611c25576020806101c052600d610160527f72616d702074696d653c6d696e0000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b611c306101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a052614e206004351015611cd7576020806102205260056101c0527f413c6d696e0000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6305f5e1006004351115611d5d576020806102205260056101c0527f413e6d61780000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6402540be4006024351015611de4576020806102205260096101c0527f67616d6d613c6d696e00000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6702c2fd72164d80006024351115611e6e576020806102205260096101c0527f67616d6d653e6d617800000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b600435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610160518015615a9357808204905090506101c052678ac7230489e800006101c0511115611f2f5760208061024052600a6101e0527f4120746f6f206869676800000000000000000000000000000000000000000000610200526101e08161024001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c0511015611fba576020806102405260096101e0527f4120746f6f206c6f770000000000000000000000000000000000000000000000610200526101e08161024001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b602435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610180518015615a9357808204905090506101c052678ac7230489e800006101c051111561207b5760208061024052600e6101e0527f67616d6d6120746f6f2068696768000000000000000000000000000000000000610200526101e08161024001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c05110156121065760208061024052600d6101e0527f67616d6d6120746f6f206c6f7700000000000000000000000000000000000000610200526101e08161024001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b6101a0516006554260075560043560801b6101e0526024356101e051176101e0526044356009556101e0516008557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd0561016051610200526004356102205261018051610240526024356102605242610280526044356102a05260c0610200a1005b63d046b4ca8118612c4d5734615a9357600e5460405260206040f35b636dbcf350811861253d5760c436103417615a93575f5c600114615a935760015f5d6121cd6159af565b60606004610160376018546040526121e66102206130f4565b6102206060816101c05e506402540be40061018051111561220e576101e05161018052612294565b6207a12061018051101561229457602080610280526004610220527f2166656500000000000000000000000000000000000000000000000000000000610240526102208161028001602482825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b6402540be40161016051106122ac576101c051610160525b6101805161016051111561233257602080610280526008610220527f216d69642d666565000000000000000000000000000000000000000000000000610240526102208161028001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b670de0b6b3a763ffff6101a051111561235257610200516101a0526123d2565b6101a0516123d25760208061028052600a610220527f216665655f67616d6d6100000000000000000000000000000000000000000000610240526102208161028001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b606061016060405e6123e5610220615a7d565b6102205160185560606064610220376017546040526124056102e06130f4565b6102e06060816102805e50670de0b6b3a7640001610220511061242b5761028051610220525b670de0b6b3a76400016102405110612446576102a051610240525b620d505d610260511115612461576102c051610260526124e5565b60576102605110156124e55760208061034052600b6102e0527f4d413c36302f6c6e283229000000000000000000000000000000000000000000610300526102e08161034001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610320528060040161033cfd5b606061022060405e6124f86102e0615a7d565b6102e0516017557fa32137411fc7c20db359079cd84af0e2cad58cd7a182a8a5e23e08e554e88bf060606101606102e05e60606102206103405e60c06102e0a15f5f5d005b6354fd4d508118612c4d5734615a935760208060805260076040527f76322e312e306400000000000000000000000000000000000000000000000000606052604081608001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b63f5b2f0168118612c4d57602436103417615a93576125cf6159af565b60043561264e576020806101c0526009610160527f216475726174696f6e000000000000000000000000000000000000000000000061018052610160816101c001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600c557fb2cf7972e8e7c2db8a62b4c568cf133a24bf5910b2603ad8811e6bfc9a865322600435610160526020610160a1005b638325c0028118612c4d57606436103417615a93576126a16159af565b600435612720576020806101c0526007610160527f21706572696f640000000000000000000000000000000000000000000000000061018052610160816101c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60243561279f576020806101c052600a610160527f217468726573686f6c640000000000000000000000000000000000000000000061018052610160816101c001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60443561281e576020806101c052600b610160527f216d61785f73686172657300000000000000000000000000000000000000000061018052610160816101c001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600f55602435601055604435600b557f6db4ade9cd8d6e671d6d713ab38d8889f9e3d4bbb319ca3389a516cf4efcf19d60606004610160376060610160a1005b633217902f8118612c4d57602436103417615a935761287e6159af565b6402540be4006004351115612905576020806101c052600d610160527f61646d696e5f6665653e4d41580000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b6004356019557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b74600435610160526020610160a1005b636fe26a348118612c4d57604436103417615a93576004358060a01c615a9357610160526024358060a01c615a9357610180526129766159af565b610160511561298657600161298d565b6101805115155b612a09576020806102005260096101a0527f21636f6e747261637400000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6101605115612a1a57610160516001555b6101805115612a2a57610180515f555b7f567da2faedc99f951794c7dc379b4906dcdd1d016fedb16916d1e9623901f21660406101606101a05e60406101a0a1005b63ed6c15468118612c4d5734615a93575f5460405260206040f35b63c45a01558118612a955734615a93576020615ba760403960206040f35b637ba1a74d8118612ab15734615a935760145460405260206040f35b63e36164058118612c4d5734615a935760185460405260206040f35b63204fe3d58118612c4d5734615a935760065460405260206040f35b63f30cfad58118612b055734615a935760085460405260206040f35b6318160ddd8118612c4d5734615a9357601d5460405260206040f35b63a3bdf1b78118612c4d5734615a9357600a5460405260206040f35b633d2699f28118612c4d5734615a9357600b5460405260206040f35b630decf4a28118612c4d5734615a9357600c5460405260206040f35b631f88619c8118612c4d5734615a9357600f5460405260206040f35b63fee3f7f98118612c4d5734615a935760195460405260206040f35b6395d89b418118612c4d5734615a9357602080604052806040016040615c2782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63313ce5678118612c4d5734615a9357601260405260206040f35b6370a082318118612c4d57602436103417615a93576004358060a01c615a9357604052601b6040516020525f5260405f205460605260206060f35b5f5ffd5b60206040516002811015615a935760051b604001615b27015f395f516370a0823160e0523061010052602060e0602460fc845afa612c91573d5f5f3e3d5ffd5b60203d10615a935760e090505160c05260a05115612d815760c0516040516002811015615a935760110154808203828111615a93579050905060e05260605160e0511015612d5157602080610160526006610100527f21636f696e730000000000000000000000000000000000000000000000000000610120526101008161016001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610140528060040161015cfd5b6040516002811015615a9357601101805460e051808201828110615a93579050905081555060e051815250612f22565b60206040516002811015615a935760051b604001615b27015f395f516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc5f855af1612dd0573d5f5f3e3d5ffd5b3d612de757803b15615a9357600161016052612e0f565b3d602081183d60201002188060e00161010011615a935760e0518060011c615a935761016052505b610160905051612e91576020806101e052600d610180527f217472616e7366657246726f6d000000000000000000000000000000000000006101a052610180816101e001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b60206040516002811015615a935760051b604001615b27015f395f516370a082316101005230610120526020610100602461011c845afa612ed4573d5f5f3e3d5ffd5b60203d10615a935761010090505160c051808203828111615a93579050905060e0526040516002811015615a9357601101805460e051808201828110615a93579050905081555060e0518152505b565b6009546040526008546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a05260405142101561306d5760065460c05260075460e05260405160e051808203828111615a9357905090506040524260e051808203828111615a93579050905060e05260405160e051808203828111615a9357905090506101005260c05160801c61010051808202811583838304141715615a93579050905060a05160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a93578082049050905060a0526fffffffffffffffffffffffffffffffff60c0511661010051808202811583838304141715615a93579050905060805160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a9357808204905090506080525b60a0518152608051602082015250565b6040516020615b275f395f51808202811583838304141715615a9357905090508152670de0b6b3a76400006060516020615b475f395f51808202811583838304141715615a935790509050608051808202811583838304141715615a93579050905004602082015250565b60055460095411815250565b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b6018546040526131416101006130f4565b61010060608160a05e50606051608051808201828110615a93579050905061010052606051673782dace9d900000810281673782dace9d900000820418615a93579050610100518015615a935780820490509050608051808202811583838304141715615a935790509050610100518015615a9357808204905090506101005260e05161010051808202811583838304141715615a935790509050670de0b6b3a764000060e05161010051808202811583838304141715615a93579050905004670de0b6b3a76400008101818110615a9357905061010051808203828111615a9357905090508015615a93578082049050905061010052670de0b6b3a764000060a05161010051808202811583838304141715615a93579050905060c0516101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050808201828110615a93579050905004815250565b600a546060526060516132bd575f8152506133db565b42600d54808203828111615a935790509050608052606051606051608051808202811583838304141715615a935790509050600c548015615a9357808204905090508082811882841002189050905060a0526040516133215760a0518152506133db565b5f60c052600e5460e0524260e05111156133945760e05142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905060c0525b60a05160c05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050670de0b6b3a7640000810490508152505b565b604051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060011c9050606051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061344e578160801c91508060401b90505b6901000000000000000000821061346c578160401c91508060201b90505b650100000000008210613486578160201c91508060101b90505b6301000000821061349e578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090508015615a935780820490509050815250565b6003546101a0526004546101c0526002546101e05260175460405261353d6102606130f4565b6102606060816102005e506135536102806130e8565b6102805161026052600554610280525f6102a052426102805110156136a3575f546381d18d876102c05261024051610280514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050048060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f036102e05260206102c060246102dc845afa6135f4573d5f5f3e3d5ffd5b60203d10615a93576102c09050516102a052670de0b6b3a76400006101c0516101e0518060011b818160011c18615a93579050808281188284100218905090506102a05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a9357905090506101a0516102a051808202811583838304141715615a935790509050808201828110615a935790509050046101a0526101a051600355426005555b670de0b6b3a76400005f54637e0e395e6102c05260406101406102e05e610180516103205260406101006103405e60206102c060a46102dc845afa6136ea573d5f5f3e3d5ffd5b60203d10615a93576102c09050516101e051808202811583838304141715615a93579050905004600455601d546102c052600160405261372b6103006132a7565b610300516102e0526102c0516102e051808203828111615a9357905090506103005260165461032052610180516040526101e05160605261376d6103606133dd565b610360516103405261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a935780820490509050610360526103205161036051101561383a576102605161383a576020806103e0526017610380527f7669727475616c207072696365206465637265617365640000000000000000006103a052610380816103e001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103c052806004016103dcfd5b60145461036051808201828110615a93579050905061032051808203828111615a935790509050610380526103805160145561038051670de0b6b3a76400008101818110615a935790508060011c905080670de0b6b3a7640000811882670de0b6b3a764000011021890506103a05261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610300518015615a9357808204905090506103c052610360516103c0511015613966576020806104405260116103e0527f6e6567617469766520646f6e6174696f6e000000000000000000000000000000610400526103e08161044001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610420528060040161043cfd5b6103a05161020051808201828110615a9357905090506103c0511161398b575f613992565b4261028051105b15613de0576101e051670de0b6b3a76400006101a05102046103e052670de0b6b3a76400016103e05110156139d8576103e051670de0b6b3a7640000036103e0526139eb565b670de0b6b3a76400006103e051036103e0525b6102205160056103e051048082811882841102189050905061040052610400516103e0511115613de0576103e0516101e051610400516103e05103808202811583838304141715615a935790509050610400516101a051808202811583838304141715615a935790509050808201828110615a935790509050046104205261014051610440526101e0516101605161042051808202811583838304141715615a93579050905004610460525f5463e68647666104a05260406101006104c05e60406104406105005e5f6105405260206104a060a46104bc845afa613ad1573d5f5f3e3d5ffd5b60203d10615a93576104a0905051610480526104805160405261042051606052613afc6104c06133dd565b6104c0516104a0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a9357808204905090506104c0525f6104e0526103a051610360518082811882841102189050905061050052610500516104c0511015613c8b576104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610500518015615a935780820490509050610520526102c0516105205110613c23576020806105a052601a610540527f747765616b656420737570706c79206d75737420736872696e6b00000000000061056052610540816105a001603a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610580528060040161059cfd5b610520516102c051036102e051808281188284100218905090506104e0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0516104e051808203828111615a9357905090508015615a9357808204905090506104c0525b670de0b6b3a76400016104c0511015613ca4575f613caf565b6103a0516104c05110155b15613de057610480516013556104c051601655610420516002556104e05115613dd4575f604052613ce16105406132a7565b61054051610520526102e05161054052610520516104e05161052051808202811583838304141715615a935790509050610540518015615a935780820490509050808203828111615a93579050905061056052600a546104e051808203828111615a935790509050610580525f6105a0526105805115613d6657610560511515613d68565b5f5b15613d9e5761056051600c54808202811583838304141715615a935790509050610580518015615a9357808204905090506105a0525b61058051600a55601d546104e051808203828111615a935790509050601d55426105a051808203828111615a935790509050600d555b61042051815250613df6565b61018051601355610360516016556101e0518152505b565b610620516106005118613e7d576020806106e0526009610680527f73616d6520636f696e00000000000000000000000000000000000000000000006106a052610680816106e001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b61064051613efd576020806106e0526007610680527f7a65726f206478000000000000000000000000000000000000000000000000006106a052610680816106e001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b613f086106c0612f24565b6106c06040816106805e506011546106c0526012546106e0525f61070052610620516002811015615a935760051b6106c0015161072052610600516002811015615a935760051b6106c0015161064051808203828111615a935790509050610740526002546107605260406106c060405e61076051608052613f8b6107c061307d565b6107c06040816107805e50613fa16107c06130e8565b6107c051156140ac57610740516020610600516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061074052610600511561401457670de0b6b3a76400006107405161076051808202811583838304141715615a93579050905004610740525b610600516002811015615a935760051b61078001516107e05261074051610600516002811015615a935760051b61078001525f5463e68647666108005260406106806108205e60406107806108605e5f6108a052602061080060a461081c845afa614081573d5f5f3e3d5ffd5b60203d10615a93576108009050516013556107e051610600516002811015615a935760051b61078001525b6013546107c0525f546343d188fb6108205260406106806108405e60406107806108805e6107c0516108c052610620516108e052604061082060c461083c845afa6140f9573d5f5f3e3d5ffd5b60403d10615a935761082090506040816107e05e50610620516002811015615a935760051b61078001516107e051808203828111615a93579050905061070052610620516002811015615a935760051b61078001805161070051808203828111615a9357905090508152506107005160018103818111615a935790506107005261062051156141ba5761070051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610760518015615a935780820490509050610700525b610700516020610620516002811015615a935760051b615b27015f395f518015615a935780820490509050610700526402540be400604061078060605e614202610840613130565b6108405161070051808202811583838304141715615a93579050905004610820526107005161082051808203828111615a93579050905061070052610660516107005110156142c3576020806108a0526008610840527f736c69707061676500000000000000000000000000000000000000000000000061086052610840816108a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610880528060040161089cfd5b6107205161070051808203828111615a93579050905061072052610720516020610620516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061072052610620511561434757670de0b6b3a76400006107205161076051808202811583838304141715615a93579050905004610720525b61072051610620516002811015615a935760051b61078001525f5463e68647666108405260406106806108605e60406107806108a05e610800516108e052602061084060a461085c845afa61439e573d5f5f3e3d5ffd5b60203d10615a93576108409050516107c05260406106806101005e60406107806101405e6107c051610180526143d5610840613517565b610840516107605261070051815261082051602082015261076051604082015250565b6040516002811015615a93576011018054606051808203828111615a93579050905081555060206040516002811015615a935760051b604001615b27015f395f5163a9059cbb60a05260805160c05260605160e052602060a0604460bc5f855af1614465573d5f5f3e3d5ffd5b3d61447c57803b15615a93576001610100526144a3565b3d602081183d60201002188060a00160c011615a935760a0518060011c615a935761010052505b61010090505161452557602080610180526009610120527f217472616e736665720000000000000000000000000000000000000000000000610140526101208161018001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610160528060040161017cfd5b565b61453160c06130e8565b60c0516145435760135481525061458a565b5f5463e686476660e052604060406101005e604060806101405e5f61018052602060e060a460fc845afa614579573d5f5f3e3d5ffd5b60203d10615a935760e09050518152505b565b6101e051156145a157620186a0815250614859565b60406101606102405e61022051156145bb57604036610240375b60115461024051808203828111615a9357905090506020615b275f395f51808202811583838304141715615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060125461026051808203828111615a9357905090506020615b475f395f51808202811583838304141715615a9357905090508015615a93578082049050905061028052604061016060405e610280516080526146686102a061307d565b6102a06040816102e05e5060406102e06101605e60406101a060605e61468f6102c0613130565b6102c05160011b60021c6102a0525f6102c0525f6002905b8060051b61016001516102e0526102c0516102e051808201828110615a9357905090506102c0526001018181186146a75750506102c05160011c6102e0525f610300525f6002905b8060051b6101600151610320526102e051610320511161472d5761030051610320516102e05103808201828110615a9357905090506103005261474d565b610300516102e0516103205103808201828110615a935790509050610300525b6001018181186146ef5750505f61032052610200511561480757600e546103405242610340511115614807576103405142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905061036052610360516102a051808202811583838304141715615a935790509050670de0b6b3a764000081049050610320525b6102a05161030051808202811583838304141715615a9357905090506102c0518015615a935780820490509050620186a08101818110615a9357905061032051808201828110615a9357905090508152505b565b601d54606051808201828110615a935790509050601d55601b6040516020525f5260405f208054606051808201828110615a9357905090508155506040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54606051808203828111615a935790509050601d55601b6040516020525f5260405f208054606051808203828111615a9357905090508155505f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54600a541415614aac576020615ba75f395f5163cab4d3db610200526020610200600461021c845afa614976573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0526101e051614a10576020615ba75f395f5163f851a440610200526020610200600461021c845afa6149dc573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0525b60115461020052601254610220525f6002905b806102405261024051604052610240516002811015615a935760051b61020001516060526101e051608052614a566143f8565b600101818118614a235750505f600a555f601d555f6013555f600e556101e0517fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102006102405e5f610280526060610240a25b565b601a546101e0526201517f6101e05142031115614ad957614ad06102006130e8565b61020051614adc565b60015b614e68576014546102005260155461022052601d546102405261022051610200511115614b1757670de0b6b3a763ffff610240511115614b1a565b60015b614e685760135461026052601654610280526002546102a0526020615ba75f395f5163cab4d3db6102e05260206102e060046102fc845afa614b5e573d5f5f3e3d5ffd5b3d602081183d6020100218806102e00161030011615a93576102e0518060a01c615a935761032052506103209050516102c0526011546102e052601254610300526404a817c800610220516102005103601954808202811583838304141715615a93579050905004610320525f610340526102c05115614be357610320511515614be5565b5f5b15614cb15761028051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102805161032051808203828111615a9357905090508015615a935780820490509050670de0b6b3a76400008103818111615a9357905061036052610340516102405161036051808202811583838304141715615a935790509050670de0b6b3a764000081049050808201828110615a9357905090506103405261020051610320518060011b818160011c18615a93579050808203828111615a935790509050610200525b6102405161034051808201828110615a93579050905061036052610260516040526102a051606052614ce46103806133dd565b61038051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610360518015615a93578082049050905061028052670de0b6b3a763ffff610280511115614e68576102005160145542601a556102805160165561026051610360516102605161034051808202811583838304141715615a93579050905004808203828111615a93579050905060135561022051610200511115614d8c57610200516015555b604036610380376103405115614e68575f6002905b806103c0526103c0516002811015615a935760051b6102e0015161034051808202811583838304141715615a935790509050610360518015615a9357808204905090506103c0516002811015615a935760051b61038001526103c0516040526103c0516002811015615a935760051b61038001516060526102c051608052614e276143f8565b600101818118614da15750506102c0517f3bbd5f2f4711532d6e9ee88dfdf2f1468e9a4c3ae5e14d2e1a67bf4242d008d060406103806103c05e60406103c0a25b565b601d5461042052610420516103c0511115614ef7576020806104a0526007610440527f21616d6f756e740000000000000000000000000000000000000000000000000061046052610440816104a001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610480528060040161049cfd5b6103e0518060010360018111615a935790506104405260115461046052601254610480526002546104a052604061046060405e6104a051608052614f3c61050061307d565b6105006040816104c05e50604061038060405e60406104c060805e614f62610520614527565b6105205161050052610420516103c05161050051808202811583838304141715615a935790509050046105205260406104c06105405e6020615b275f395f51670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610580526104a0516020615b475f395f51808202811583838304141715615a9357905090506105a0526040366105c037670de0b6b3a7640000610400516103e0516002811015615a935760051b6105800151808202811583838304141715615a935790509050046103e0516002811015615a935760051b6105c001526103e0516002811015615a935760051b6105400180516103e0516002811015615a935760051b6105c00151808203828111615a9357905090508152505f546343d188fb6106205260406103806106405e60406105406106805e6105005161052051808203828111615a9357905090506106c052610440516106e052604061062060c461063c845afa6150cf573d5f5f3e3d5ffd5b60403d10615a935761062090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050610440516002811015615a935760051b6105c0015261060051610440516002811015615a935760051b610540015260403661062037610400516103e0516002811015615a935760051b61062001526103e0516151b0576105e051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506020615b475f395f518015615a9357808204905090506104a0518015615a935780820490509050610640526151cf565b6105c0516020615b275f395f518015615a935780820490509050610620525b6106205161064051808201828110615a935790509050615261576020806106c0526007610660527f21746f6b656e730000000000000000000000000000000000000000000000000061068052610660816106c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106a052806004016106bcfd5b60406106206101605e60406105406101a05e6060366101e03761528561068061458c565b6106805161066052610520516105205161066051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050808203828111615a935790509050610520525f546343d188fb6106805260406103806106a05e60406105406106e05e6105005161052051808203828111615a935790509050610720526104405161074052604061068060c461069c845afa61532e573d5f5f3e3d5ffd5b60403d10615a935761068090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610440516002811015615a935760051b61058001518015615a9357808204905090506106805261060051610440516002811015615a935760051b61054001526106805181526105005161052051808203828111615a9357905090506020820152604081016040610540825e5061066051608082015250565b615405614aae565b615410610840612f24565b6108406040816108005e5060a0366108403760406108006103805e60606107606103c05e61543f6108e0614e6a565b6108e060a0816109805e5060406109806108405e60406109c06108805e610a00516108c0526107c0516108405110156154ea576020806109405260086108e0527f736c697070616765000000000000000000000000000000000000000000000000610900526108e08161094001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610920528060040161093cfd5b33604052610760516060526155006108e06148cd565b6108e05060406108006101005e60406108806101405e6108605161018052615529610900613517565b610900516108e0526107a0511561555157604061078060405e6107e0516080526155516143f8565b610780518060010360018111615a935790506109005261090051604052610840516060526107e0516080526155846143f8565b604036610920376107a051610780516002811015615a935760051b610920015261084051610900516002811015615a935760051b61092001526107a05161564957337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c61076051610960526109005161098052610840516109a0526108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a26156c2565b337f22f9ea3e7d7b113cb423896d2e121f96a66c17814ac7f63d69096769fa3e2a55610760516109605260406109206109805e6108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a25b6156ca61493f565b61084051815250565b608051601c6040516020525f5260405f20806060516020525f5260405f209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b60605130811461573a5780151561573c565b5f5b90506157b75760208061012052600960c0527f217265636569766572000000000000000000000000000000000000000000000060e05260c08161012001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b601b6040516020525f5260405f208054608051808203828111615a935790509050815550601b6060516020525f5260405f208054608051808201828110615a9357905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b60035460605260025460805260055460a0524260a05110156159a65760045460c0526017546040526158666101006130f4565b6101006040810190505160e0525f546381d18d876101205260a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060e0518015615a9357808204905090508060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f03610140526020610120602461013c845afa6158ff573d5f5f3e3d5ffd5b60203d10615a93576101209050516101005260c0516080518060011b818160011c18615a93579050808281188284100218905090506101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a93579050905060605161010051808202811583838304141715615a935790509050808201828110615a935790509050670de0b6b3a7640000810490508152506159ad565b6060518152505b565b6020615ba75f395f5163f851a440604052602060406004605c845afa6159d7573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a9357608052506080905051331815615a7b5760208061010052600a60a0527f6f6e6c79206f776e65720000000000000000000000000000000000000000000060c05260a08161010001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b565b60805160605160401b60405160801b1717815250565b5f80fd2c4d2c122b3d2c4d2c4d2acd185e14fc00852c4d2c4d13200c7315db104d2861001810a41adf180c0373197c2a5c2b2103bd2c4d268401942c4d0f6211950cb813e72bad15a01a3b293b2ae918ef2bf717652c4d2c4d128f11682c4d2c4d2c4d2c4d2c4d2c4d2c4d2c4d03231a59189b21a319542b5913662b752c4d10f101b32c4d2b9119a8144c2c4d2a7725b22c4d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000002540be400000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59900000000000000000000000098ee851a00abee0d95d08cf4ca2bdce32aeaaf7f00000000000000000000000000000000000000000000000000000000000000075942205742544300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086379622d77627463000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.