Vyper_contract

Deploy on Alchemy

Contract Information

The following smart contract code is not written in Solidity but in Vyper programming language. The code sets a boolean variable `is_killed` to `False`. The purpose of this code is not clear without the context of the entire contract.
More Info

Vyper_contract Source Code

# @version 0.3.1 """ @title StableSwap @author Curve.Fi @license Copyright (c) Curve.Fi, 2020 - all rights reserved """ from vyper.interfaces import ERC20 interface CurveToken: def totalSupply() -> uint256: view def mint(_to: address, _value: uint256) -> bool: nonpayable def burnFrom(_to: address, _value: uint256) -> bool: nonpayable # Events event TokenExchange: buyer: indexed(address) sold_id: int128 tokens_sold: uint256 bought_id: int128 tokens_bought: uint256 event AddLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] fees: uint256[N_COINS] invariant: uint256 token_supply: uint256 event RemoveLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] fees: uint256[N_COINS] token_supply: uint256 event RemoveLiquidityOne: provider: indexed(address) token_amount: uint256 coin_amount: uint256 token_supply: uint256 event RemoveLiquidityImbalance: provider: indexed(address) token_amounts: uint256[N_COINS] fees: uint256[N_COINS] invariant: uint256 token_supply: uint256 event CommitNewAdmin: deadline: indexed(uint256) admin: indexed(address) event NewAdmin: admin: indexed(address) event CommitNewFee: deadline: indexed(uint256) fee: uint256 admin_fee: uint256 event NewFee: fee: uint256 admin_fee: uint256 event RampA: old_A: uint256 new_A: uint256 initial_time: uint256 future_time: uint256 event StopRampA: A: uint256 t: uint256 # These constants must be set prior to compiling N_COINS: constant(int128) = 2 PRECISION_MUL: constant(uint256[N_COINS]) = [1, 1000000000000] RATES: constant(uint256[N_COINS]) = [1000000000000000000, 1000000000000000000000000000000] # fixed constants FEE_DENOMINATOR: constant(uint256) = 10 ** 10 PRECISION: constant(uint256) = 10 ** 18 # The precision to convert to MAX_ADMIN_FEE: constant(uint256) = 10 * 10 ** 9 MAX_FEE: constant(uint256) = 5 * 10 ** 9 MAX_A: constant(uint256) = 10 ** 6 MAX_A_CHANGE: constant(uint256) = 10 ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400 MIN_RAMP_TIME: constant(uint256) = 86400 coins: public(address[N_COINS]) balances: public(uint256[N_COINS]) fee: public(uint256) # fee * 1e10 admin_fee: public(uint256) # admin_fee * 1e10 owner: public(address) lp_token: public(address) A_PRECISION: constant(uint256) = 100 initial_A: public(uint256) future_A: public(uint256) initial_A_time: public(uint256) future_A_time: public(uint256) admin_actions_deadline: public(uint256) transfer_ownership_deadline: public(uint256) future_fee: public(uint256) future_admin_fee: public(uint256) future_owner: public(address) is_killed: bool kill_deadline: uint256 KILL_DEADLINE_DT: constant(uint256) = 2 * 30 * 86400 @external def __init__( _owner: address, _coins: address[N_COINS], _pool_token: address, _A: uint256, _fee: uint256, _admin_fee: uint256 ): """ @notice Contract constructor @param _owner Contract owner address @param _coins Addresses of ERC20 conracts of coins @param _pool_token Address of the token representing LP share @param _A Amplification coefficient multiplied by n * (n - 1) @param _fee Fee to charge for exchanges @param _admin_fee Admin fee """ for i in range(N_COINS): assert _coins[i] != ZERO_ADDRESS self.coins = _coins self.initial_A = _A * A_PRECISION self.future_A = _A * A_PRECISION self.fee = _fee self.admin_fee = _admin_fee self.owner = _owner self.kill_deadline = block.timestamp + KILL_DEADLINE_DT self.lp_token = _pool_token @view @internal def _A() -> uint256: """ Handle ramping A up or down """ t1: uint256 = self.future_A_time A1: uint256 = self.future_A if block.timestamp < t1: A0: uint256 = self.initial_A t0: uint256 = self.initial_A_time # Expressions in uint256 cannot have negative numbers, thus "if" if A1 > A0: return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0) else: return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0) else: # when t1 == 0 or block.timestamp >= t1 return A1 @view @external def A() -> uint256: return self._A() / A_PRECISION @view @external def A_precise() -> uint256: return self._A() @view @internal def _xp() -> uint256[N_COINS]: result: uint256[N_COINS] = RATES for i in range(N_COINS): result[i] = result[i] * self.balances[i] / PRECISION return result @pure @internal def _xp_mem(_balances: uint256[N_COINS]) -> uint256[N_COINS]: result: uint256[N_COINS] = RATES for i in range(N_COINS): result[i] = result[i] * _balances[i] / PRECISION return result @pure @internal def _get_D(_xp: uint256[N_COINS], _amp: uint256) -> uint256: """ D invariant calculation in non-overflowing integer operations iteratively A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i)) Converging solution: D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1) """ S: uint256 = 0 Dprev: uint256 = 0 for _x in _xp: S += _x if S == 0: return 0 D: uint256 = S Ann: uint256 = _amp * N_COINS for _i in range(255): D_P: uint256 = D for _x in _xp: D_P = D_P * D / (_x * N_COINS) # If division by 0, this will be borked: only withdrawal will work. And that is good Dprev = D D = (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P) # Equality with the precision of 1 if D > Dprev: if D - Dprev <= 1: return D else: if Dprev - D <= 1: return D # convergence typically occurs in 4 rounds or less, this should be unreachable! # if it does happen the pool is borked and LPs can withdraw via `remove_liquidity` raise @view @internal def _get_D_mem(_balances: uint256[N_COINS], _amp: uint256) -> uint256: return self._get_D(self._xp_mem(_balances), _amp) @view @external def get_virtual_price() -> uint256: """ @notice The current virtual price of the pool LP token @dev Useful for calculating profits @return LP token virtual price normalized to 1e18 """ D: uint256 = self._get_D(self._xp(), self._A()) # D is in the units similar to DAI (e.g. converted to precision 1e18) # When balanced, D = n * x_u - total virtual value of the portfolio token_supply: uint256 = ERC20(self.lp_token).totalSupply() return D * PRECISION / token_supply @view @external def calc_token_amount(_amounts: uint256[N_COINS], _is_deposit: bool) -> uint256: """ @notice Calculate addition or reduction in token supply from a deposit or withdrawal @dev This calculation accounts for slippage, but not fees. Needed to prevent front-running, not for precise calculations! @param _amounts Amount of each coin being deposited @param _is_deposit set True for deposits, False for withdrawals @return Expected amount of LP tokens received """ amp: uint256 = self._A() balances: uint256[N_COINS] = self.balances D0: uint256 = self._get_D_mem(balances, amp) for i in range(N_COINS): if _is_deposit: balances[i] += _amounts[i] else: balances[i] -= _amounts[i] D1: uint256 = self._get_D_mem(balances, amp) token_amount: uint256 = CurveToken(self.lp_token).totalSupply() diff: uint256 = 0 if _is_deposit: diff = D1 - D0 else: diff = D0 - D1 return diff * token_amount / D0 @external @nonreentrant('lock') def add_liquidity(_amounts: uint256[N_COINS], _min_mint_amount: uint256) -> uint256: """ @notice Deposit coins into the pool @param _amounts List of amounts of coins to deposit @param _min_mint_amount Minimum amount of LP tokens to mint from the deposit @return Amount of LP tokens received by depositing """ assert not self.is_killed # dev: is killed amp: uint256 = self._A() old_balances: uint256[N_COINS] = self.balances # Initial invariant D0: uint256 = self._get_D_mem(old_balances, amp) lp_token: address = self.lp_token token_supply: uint256 = CurveToken(lp_token).totalSupply() new_balances: uint256[N_COINS] = old_balances for i in range(N_COINS): if token_supply == 0: assert _amounts[i] > 0 # dev: initial deposit requires all coins # balances store amounts of c-tokens new_balances[i] += _amounts[i] # Invariant after change D1: uint256 = self._get_D_mem(new_balances, amp) assert D1 > D0 # We need to recalculate the invariant accounting for fees # to calculate fair user's share D2: uint256 = D1 fees: uint256[N_COINS] = empty(uint256[N_COINS]) mint_amount: uint256 = 0 if token_supply > 0: # Only account for fees if we are not the first to deposit fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1)) admin_fee: uint256 = self.admin_fee for i in range(N_COINS): ideal_balance: uint256 = D1 * old_balances[i] / D0 difference: uint256 = 0 new_balance: uint256 = new_balances[i] if ideal_balance > new_balance: difference = ideal_balance - new_balance else: difference = new_balance - ideal_balance fees[i] = fee * difference / FEE_DENOMINATOR self.balances[i] = new_balance - (fees[i] * admin_fee / FEE_DENOMINATOR) new_balances[i] -= fees[i] D2 = self._get_D_mem(new_balances, amp) mint_amount = token_supply * (D2 - D0) / D0 else: self.balances = new_balances mint_amount = D1 # Take the dust if there was any assert mint_amount >= _min_mint_amount, "Slippage screwed you" # Take coins from the sender for i in range(N_COINS): if _amounts[i] > 0: # "safeTransferFrom" which works for ERC20s which return bool or not _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transferFrom(address,address,uint256)"), convert(msg.sender, bytes32), convert(self, bytes32), convert(_amounts[i], bytes32), ), max_outsize=32, ) if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer # end "safeTransferFrom" # Mint pool tokens CurveToken(lp_token).mint(msg.sender, mint_amount) log AddLiquidity(msg.sender, _amounts, fees, D1, token_supply + mint_amount) return mint_amount @view @internal def _get_y(i: int128, j: int128, x: uint256, _xp: uint256[N_COINS]) -> uint256: """ Calculate x[j] if one makes x[i] = x Done by solving quadratic equation iteratively. x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) x_1**2 + b*x_1 = c x_1 = (x_1**2 + c) / (2*x_1 + b) """ # x in the input is converted to the same price/precision assert i != j # dev: same coin assert j >= 0 # dev: j below zero assert j < N_COINS # dev: j above N_COINS # should be unreachable, but good for safety assert i >= 0 assert i < N_COINS A: uint256 = self._A() D: uint256 = self._get_D(_xp, A) Ann: uint256 = A * N_COINS c: uint256 = D S: uint256 = 0 _x: uint256 = 0 y_prev: uint256 = 0 for _i in range(N_COINS): if _i == i: _x = x elif _i != j: _x = _xp[_i] else: continue S += _x c = c * D / (_x * N_COINS) c = c * D * A_PRECISION / (Ann * N_COINS) b: uint256 = S + D * A_PRECISION / Ann # - D y: uint256 = D for _i in range(255): y_prev = y y = (y*y + c) / (2 * y + b - D) # Equality with the precision of 1 if y > y_prev: if y - y_prev <= 1: return y else: if y_prev - y <= 1: return y raise @view @external def get_dy(i: int128, j: int128, _dx: uint256) -> uint256: xp: uint256[N_COINS] = self._xp() rates: uint256[N_COINS] = RATES x: uint256 = xp[i] + (_dx * rates[i] / PRECISION) y: uint256 = self._get_y(i, j, x, xp) dy: uint256 = xp[j] - y - 1 fee: uint256 = self.fee * dy / FEE_DENOMINATOR return (dy - fee) * PRECISION / rates[j] @external @nonreentrant('lock') def exchange(i: int128, j: int128, _dx: uint256, _min_dy: uint256) -> uint256: """ @notice Perform an exchange between two coins @dev Index values can be found via the `coins` public getter method @param i Index value for the coin to send @param j Index valie of the coin to recieve @param _dx Amount of `i` being exchanged @param _min_dy Minimum amount of `j` to receive @return Actual amount of `j` received """ assert not self.is_killed # dev: is killed old_balances: uint256[N_COINS] = self.balances xp: uint256[N_COINS] = self._xp_mem(old_balances) rates: uint256[N_COINS] = RATES x: uint256 = xp[i] + _dx * rates[i] / PRECISION y: uint256 = self._get_y(i, j, x, xp) dy: uint256 = xp[j] - y - 1 # -1 just in case there were some rounding errors dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR # Convert all to real units dy = (dy - dy_fee) * PRECISION / rates[j] assert dy >= _min_dy, "Exchange resulted in fewer coins than expected" dy_admin_fee: uint256 = dy_fee * self.admin_fee / FEE_DENOMINATOR dy_admin_fee = dy_admin_fee * PRECISION / rates[j] # Change balances exactly in same way as we change actual ERC20 coin amounts self.balances[i] = old_balances[i] + _dx # When rounding errors happen, we undercharge admin fee in favor of LP self.balances[j] = old_balances[j] - dy - dy_admin_fee _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transferFrom(address,address,uint256)"), convert(msg.sender, bytes32), convert(self, bytes32), convert(_dx, bytes32), ), max_outsize=32, ) if len(_response) > 0: assert convert(_response, bool) _response = raw_call( self.coins[j], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(dy, bytes32), ), max_outsize=32, ) if len(_response) > 0: assert convert(_response, bool) log TokenExchange(msg.sender, i, _dx, j, dy) return dy @external @nonreentrant('lock') def remove_liquidity(_amount: uint256, _min_amounts: uint256[N_COINS]) -> uint256[N_COINS]: """ @notice Withdraw coins from the pool @dev Withdrawal amounts are based on current deposit ratios @param _amount Quantity of LP tokens to burn in the withdrawal @param _min_amounts Minimum amounts of underlying coins to receive @return List of amounts of coins that were withdrawn """ lp_token: address = self.lp_token total_supply: uint256 = CurveToken(lp_token).totalSupply() amounts: uint256[N_COINS] = empty(uint256[N_COINS]) for i in range(N_COINS): old_balance: uint256 = self.balances[i] value: uint256 = old_balance * _amount / total_supply assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected" self.balances[i] = old_balance - value amounts[i] = value _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(value, bytes32), ), max_outsize=32, ) if len(_response) > 0: assert convert(_response, bool) CurveToken(lp_token).burnFrom(msg.sender, _amount) # dev: insufficient funds log RemoveLiquidity(msg.sender, amounts, empty(uint256[N_COINS]), total_supply - _amount) return amounts @external @nonreentrant('lock') def remove_liquidity_imbalance(_amounts: uint256[N_COINS], _max_burn_amount: uint256) -> uint256: """ @notice Withdraw coins from the pool in an imbalanced amount @param _amounts List of amounts of underlying coins to withdraw @param _max_burn_amount Maximum amount of LP token to burn in the withdrawal @return Actual amount of the LP token burned in the withdrawal """ assert not self.is_killed # dev: is killed amp: uint256 = self._A() old_balances: uint256[N_COINS] = self.balances D0: uint256 = self._get_D_mem(old_balances, amp) new_balances: uint256[N_COINS] = old_balances for i in range(N_COINS): new_balances[i] -= _amounts[i] D1: uint256 = self._get_D_mem(new_balances, amp) fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1)) admin_fee: uint256 = self.admin_fee fees: uint256[N_COINS] = empty(uint256[N_COINS]) for i in range(N_COINS): new_balance: uint256 = new_balances[i] ideal_balance: uint256 = D1 * old_balances[i] / D0 difference: uint256 = 0 if ideal_balance > new_balance: difference = ideal_balance - new_balance else: difference = new_balance - ideal_balance fees[i] = fee * difference / FEE_DENOMINATOR self.balances[i] = new_balance - (fees[i] * admin_fee / FEE_DENOMINATOR) new_balances[i] = new_balance - fees[i] D2: uint256 = self._get_D_mem(new_balances, amp) lp_token: address = self.lp_token token_supply: uint256 = CurveToken(lp_token).totalSupply() token_amount: uint256 = (D0 - D2) * token_supply / D0 assert token_amount != 0 # dev: zero tokens burned token_amount += 1 # In case of rounding errors - make it unfavorable for the "attacker" assert token_amount <= _max_burn_amount, "Slippage screwed you" CurveToken(lp_token).burnFrom(msg.sender, token_amount) # dev: insufficient funds for i in range(N_COINS): if _amounts[i] != 0: _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(_amounts[i], bytes32), ), max_outsize=32, ) if len(_response) > 0: assert convert(_response, bool) log RemoveLiquidityImbalance(msg.sender, _amounts, fees, D1, token_supply - token_amount) return token_amount @pure @internal def _get_y_D(A: uint256, i: int128, _xp: uint256[N_COINS], D: uint256) -> uint256: """ Calculate x[i] if one reduces D from being calculated for xp to D Done by solving quadratic equation iteratively. x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) x_1**2 + b*x_1 = c x_1 = (x_1**2 + c) / (2*x_1 + b) """ # x in the input is converted to the same price/precision assert i >= 0 # dev: i below zero assert i < N_COINS # dev: i above N_COINS Ann: uint256 = A * N_COINS c: uint256 = D S: uint256 = 0 _x: uint256 = 0 y_prev: uint256 = 0 for _i in range(N_COINS): if _i != i: _x = _xp[_i] else: continue S += _x c = c * D / (_x * N_COINS) c = c * D * A_PRECISION / (Ann * N_COINS) b: uint256 = S + D * A_PRECISION / Ann y: uint256 = D for _i in range(255): y_prev = y y = (y*y + c) / (2 * y + b - D) # Equality with the precision of 1 if y > y_prev: if y - y_prev <= 1: return y else: if y_prev - y <= 1: return y raise @view @internal def _calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> (uint256, uint256, uint256): # First, need to calculate # * Get current D # * Solve Eqn against y_i for D - _token_amount amp: uint256 = self._A() xp: uint256[N_COINS] = self._xp() D0: uint256 = self._get_D(xp, amp) total_supply: uint256 = CurveToken(self.lp_token).totalSupply() D1: uint256 = D0 - _token_amount * D0 / total_supply new_y: uint256 = self._get_y_D(amp, i, xp, D1) xp_reduced: uint256[N_COINS] = xp fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1)) for j in range(N_COINS): dx_expected: uint256 = 0 if j == i: dx_expected = xp[j] * D1 / D0 - new_y else: dx_expected = xp[j] - xp[j] * D1 / D0 xp_reduced[j] -= fee * dx_expected / FEE_DENOMINATOR dy: uint256 = xp_reduced[i] - self._get_y_D(amp, i, xp_reduced, D1) precisions: uint256[N_COINS] = PRECISION_MUL dy = (dy - 1) / precisions[i] # Withdraw less to account for rounding errors dy_0: uint256 = (xp[i] - new_y) / precisions[i] # w/o fees return dy, dy_0 - dy, total_supply @view @external def calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> uint256: """ @notice Calculate the amount received when withdrawing a single coin @param _token_amount Amount of LP tokens to burn in the withdrawal @param i Index value of the coin to withdraw @return Amount of coin received """ return self._calc_withdraw_one_coin(_token_amount, i)[0] @external @nonreentrant('lock') def remove_liquidity_one_coin(_token_amount: uint256, i: int128, _min_amount: uint256) -> uint256: """ @notice Withdraw a single coin from the pool @param _token_amount Amount of LP tokens to burn in the withdrawal @param i Index value of the coin to withdraw @param _min_amount Minimum amount of coin to receive @return Amount of coin received """ assert not self.is_killed # dev: is killed dy: uint256 = 0 dy_fee: uint256 = 0 total_supply: uint256 = 0 dy, dy_fee, total_supply = self._calc_withdraw_one_coin(_token_amount, i) assert dy >= _min_amount, "Not enough coins removed" self.balances[i] -= (dy + dy_fee * self.admin_fee / FEE_DENOMINATOR) CurveToken(self.lp_token).burnFrom(msg.sender, _token_amount) # dev: insufficient funds _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(dy, bytes32), ), max_outsize=32, ) if len(_response) > 0: assert convert(_response, bool) log RemoveLiquidityOne(msg.sender, _token_amount, dy, total_supply - _token_amount) return dy ### Admin functions ### @external def ramp_A(_future_A: uint256, _future_time: uint256): assert msg.sender == self.owner # dev: only owner assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME assert _future_time >= block.timestamp + MIN_RAMP_TIME # dev: insufficient time initial_A: uint256 = self._A() future_A_p: uint256 = _future_A * A_PRECISION assert _future_A > 0 and _future_A < MAX_A if future_A_p < initial_A: assert future_A_p * MAX_A_CHANGE >= initial_A else: assert future_A_p <= initial_A * MAX_A_CHANGE self.initial_A = initial_A self.future_A = future_A_p self.initial_A_time = block.timestamp self.future_A_time = _future_time log RampA(initial_A, future_A_p, block.timestamp, _future_time) @external def stop_ramp_A(): assert msg.sender == self.owner # dev: only owner current_A: uint256 = self._A() self.initial_A = current_A self.future_A = current_A self.initial_A_time = block.timestamp self.future_A_time = block.timestamp # now (block.timestamp < t1) is always False, so we return saved A log StopRampA(current_A, block.timestamp) @external def commit_new_fee(_new_fee: uint256, _new_admin_fee: uint256): assert msg.sender == self.owner # dev: only owner assert self.admin_actions_deadline == 0 # dev: active action assert _new_fee <= MAX_FEE # dev: fee exceeds maximum assert _new_admin_fee <= MAX_ADMIN_FEE # dev: admin fee exceeds maximum deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY self.admin_actions_deadline = deadline self.future_fee = _new_fee self.future_admin_fee = _new_admin_fee log CommitNewFee(deadline, _new_fee, _new_admin_fee) @external def apply_new_fee(): assert msg.sender == self.owner # dev: only owner assert block.timestamp >= self.admin_actions_deadline # dev: insufficient time assert self.admin_actions_deadline != 0 # dev: no active action self.admin_actions_deadline = 0 fee: uint256 = self.future_fee admin_fee: uint256 = self.future_admin_fee self.fee = fee self.admin_fee = admin_fee log NewFee(fee, admin_fee) @external def revert_new_parameters(): assert msg.sender == self.owner # dev: only owner self.admin_actions_deadline = 0 @external def commit_transfer_ownership(_owner: address): assert msg.sender == self.owner # dev: only owner assert self.transfer_ownership_deadline == 0 # dev: active transfer deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY self.transfer_ownership_deadline = deadline self.future_owner = _owner log CommitNewAdmin(deadline, _owner) @external def apply_transfer_ownership(): assert msg.sender == self.owner # dev: only owner assert block.timestamp >= self.transfer_ownership_deadline # dev: insufficient time assert self.transfer_ownership_deadline != 0 # dev: no active transfer self.transfer_ownership_deadline = 0 owner: address = self.future_owner self.owner = owner log NewAdmin(owner) @external def revert_transfer_ownership(): assert msg.sender == self.owner # dev: only owner self.transfer_ownership_deadline = 0 @view @external def admin_balances(i: uint256) -> uint256: return ERC20(self.coins[i]).balanceOf(self) - self.balances[i] @external def withdraw_admin_fees(): assert msg.sender == self.owner # dev: only owner for i in range(N_COINS): coin: address = self.coins[i] value: uint256 = ERC20(coin).balanceOf(self) - self.balances[i] if value > 0: _response: Bytes[32] = raw_call( coin, concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(value, bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) @external def donate_admin_fees(): assert msg.sender == self.owner # dev: only owner for i in range(N_COINS): self.balances[i] = ERC20(self.coins[i]).balanceOf(self) @external def kill_me(): assert msg.sender == self.owner # dev: only owner assert self.kill_deadline > block.timestamp # dev: deadline has passed self.is_killed = True @external def unkill_me(): assert msg.sender == self.owner # dev: only owner self.is_killed = False
< # @version 0.3.1
"""
@title StableSwap
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2020 - all rights reserved
"""
from vyper.interfaces import ERC20


interface CurveToken:
    def totalSupply() -> uint256: view
    def mint(_to: address, _value: uint256) -> bool: nonpayable
    def burnFrom(_to: address, _value: uint256) -> bool: nonpayable


# Events
event TokenExchange:
    buyer: indexed(address)
    sold_id: int128
    tokens_sold: uint256
    bought_id: int128
    tokens_bought: uint256

event AddLiquidity:
    provider: indexed(address)
    token_amounts: uint256[N_COINS]
    fees: uint256[N_COINS]
    invariant: uint256
    token_supply: uint256

event RemoveLiquidity:
    provider: indexed(address)
    token_amounts: uint256[N_COINS]
    fees: uint256[N_COINS]
    token_supply: uint256

event RemoveLiquidityOne:
    provider: indexed(address)
    token_amount: uint256
    coin_amount: uint256
    token_supply: uint256

event RemoveLiquidityImbalance:
    provider: indexed(address)
    token_amounts: uint256[N_COINS]
    fees: uint256[N_COINS]
    invariant: uint256
    token_supply: uint256

event CommitNewAdmin:
    deadline: indexed(uint256)
    admin: indexed(address)

event NewAdmin:
    admin: indexed(address)

event CommitNewFee:
    deadline: indexed(uint256)
    fee: uint256
    admin_fee: uint256

event NewFee:
    fee: uint256
    admin_fee: uint256

event RampA:
    old_A: uint256
    new_A: uint256
    initial_time: uint256
    future_time: uint256

event StopRampA:
    A: uint256
    t: uint256


# These constants must be set prior to compiling
N_COINS: constant(int128) = 2
PRECISION_MUL: constant(uint256[N_COINS]) = [1, 1000000000000]
RATES: constant(uint256[N_COINS]) = [1000000000000000000, 1000000000000000000000000000000]

# fixed constants
FEE_DENOMINATOR: constant(uint256) = 10 ** 10
PRECISION: constant(uint256) = 10 ** 18  # The precision to convert to

MAX_ADMIN_FEE: constant(uint256) = 10 * 10 ** 9
MAX_FEE: constant(uint256) = 5 * 10 ** 9
MAX_A: constant(uint256) = 10 ** 6
MAX_A_CHANGE: constant(uint256) = 10

ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400
MIN_RAMP_TIME: constant(uint256) = 86400

coins: public(address[N_COINS])
balances: public(uint256[N_COINS])
fee: public(uint256)  # fee * 1e10
admin_fee: public(uint256)  # admin_fee * 1e10

owner: public(address)
lp_token: public(address)

A_PRECISION: constant(uint256) = 100
initial_A: public(uint256)
future_A: public(uint256)
initial_A_time: public(uint256)
future_A_time: public(uint256)

admin_actions_deadline: public(uint256)
transfer_ownership_deadline: public(uint256)
future_fee: public(uint256)
future_admin_fee: public(uint256)
future_owner: public(address)

is_killed: bool
kill_deadline: uint256
KILL_DEADLINE_DT: constant(uint256) = 2 * 30 * 86400


@external
def __init__(
    _owner: address,
    _coins: address[N_COINS],
    _pool_token: address,
    _A: uint256,
    _fee: uint256,
    _admin_fee: uint256
):
    """
    @notice Contract constructor
    @param _owner Contract owner address
    @param _coins Addresses of ERC20 conracts of coins
    @param _pool_token Address of the token representing LP share
    @param _A Amplification coefficient multiplied by n * (n - 1)
    @param _fee Fee to charge for exchanges
    @param _admin_fee Admin fee
    """
    for i in range(N_COINS):
        assert _coins[i] != ZERO_ADDRESS
    self.coins = _coins
    self.initial_A = _A * A_PRECISION
    self.future_A = _A * A_PRECISION
    self.fee = _fee
    self.admin_fee = _admin_fee
    self.owner = _owner
    self.kill_deadline = block.timestamp + KILL_DEADLINE_DT
    self.lp_token = _pool_token


@view
@internal
def _A() -> uint256:
    """
    Handle ramping A up or down
    """
    t1: uint256 = self.future_A_time
    A1: uint256 = self.future_A

    if block.timestamp < t1:
        A0: uint256 = self.initial_A
        t0: uint256 = self.initial_A_time
        # Expressions in uint256 cannot have negative numbers, thus "if"
        if A1 > A0:
            return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0)
        else:
            return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0)

    else:  # when t1 == 0 or block.timestamp >= t1
        return A1


@view
@external
def A() -> uint256:
    return self._A() / A_PRECISION


@view
@external
def A_precise() -> uint256:
    return self._A()


@view
@internal
def _xp() -> uint256[N_COINS]:
    result: uint256[N_COINS] = RATES
    for i in range(N_COINS):
        result[i] = result[i] * self.balances[i] / PRECISION
    return result


@pure
@internal
def _xp_mem(_balances: uint256[N_COINS]) -> uint256[N_COINS]:
    result: uint256[N_COINS] = RATES
    for i in range(N_COINS):
        result[i] = result[i] * _balances[i] / PRECISION
    return result


@pure
@internal
def _get_D(_xp: uint256[N_COINS], _amp: uint256) -> uint256:
    """
    D invariant calculation in non-overflowing integer operations
    iteratively

    A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))

    Converging solution:
    D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
    """
    S: uint256 = 0
    Dprev: uint256 = 0

    for _x in _xp:
        S += _x
    if S == 0:
        return 0

    D: uint256 = S
    Ann: uint256 = _amp * N_COINS
    for _i in range(255):
        D_P: uint256 = D
        for _x in _xp:
            D_P = D_P * D / (_x * N_COINS)  # If division by 0, this will be borked: only withdrawal will work. And that is good
        Dprev = D
        D = (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
        # Equality with the precision of 1
        if D > Dprev:
            if D - Dprev <= 1:
                return D
        else:
            if Dprev - D <= 1:
                return D
    # convergence typically occurs in 4 rounds or less, this should be unreachable!
    # if it does happen the pool is borked and LPs can withdraw via `remove_liquidity`
    raise


@view
@internal
def _get_D_mem(_balances: uint256[N_COINS], _amp: uint256) -> uint256:
    return self._get_D(self._xp_mem(_balances), _amp)


@view
@external
def get_virtual_price() -> uint256:
    """
    @notice The current virtual price of the pool LP token
    @dev Useful for calculating profits
    @return LP token virtual price normalized to 1e18
    """
    D: uint256 = self._get_D(self._xp(), self._A())
    # D is in the units similar to DAI (e.g. converted to precision 1e18)
    # When balanced, D = n * x_u - total virtual value of the portfolio
    token_supply: uint256 = ERC20(self.lp_token).totalSupply()
    return D * PRECISION / token_supply


@view
@external
def calc_token_amount(_amounts: uint256[N_COINS], _is_deposit: bool) -> uint256:
    """
    @notice Calculate addition or reduction in token supply from a deposit or withdrawal
    @dev This calculation accounts for slippage, but not fees.
         Needed to prevent front-running, not for precise calculations!
    @param _amounts Amount of each coin being deposited
    @param _is_deposit set True for deposits, False for withdrawals
    @return Expected amount of LP tokens received
    """
    amp: uint256 = self._A()
    balances: uint256[N_COINS] = self.balances
    D0: uint256 = self._get_D_mem(balances, amp)
    for i in range(N_COINS):
        if _is_deposit:
            balances[i] += _amounts[i]
        else:
            balances[i] -= _amounts[i]
    D1: uint256 = self._get_D_mem(balances, amp)
    token_amount: uint256 = CurveToken(self.lp_token).totalSupply()
    diff: uint256 = 0
    if _is_deposit:
        diff = D1 - D0
    else:
        diff = D0 - D1
    return diff * token_amount / D0


@external
@nonreentrant('lock')
def add_liquidity(_amounts: uint256[N_COINS], _min_mint_amount: uint256) -> uint256:
    """
    @notice Deposit coins into the pool
    @param _amounts List of amounts of coins to deposit
    @param _min_mint_amount Minimum amount of LP tokens to mint from the deposit
    @return Amount of LP tokens received by depositing
    """
    assert not self.is_killed  # dev: is killed

    amp: uint256 = self._A()
    old_balances: uint256[N_COINS] = self.balances

    # Initial invariant
    D0: uint256 = self._get_D_mem(old_balances, amp)

    lp_token: address = self.lp_token
    token_supply: uint256 = CurveToken(lp_token).totalSupply()
    new_balances: uint256[N_COINS] = old_balances
    for i in range(N_COINS):
        if token_supply == 0:
            assert _amounts[i] > 0  # dev: initial deposit requires all coins
        # balances store amounts of c-tokens
        new_balances[i] += _amounts[i]

    # Invariant after change
    D1: uint256 = self._get_D_mem(new_balances, amp)
    assert D1 > D0

    # We need to recalculate the invariant accounting for fees
    # to calculate fair user's share
    D2: uint256 = D1
    fees: uint256[N_COINS] = empty(uint256[N_COINS])
    mint_amount: uint256 = 0
    if token_supply > 0:
        # Only account for fees if we are not the first to deposit
        fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
        admin_fee: uint256 = self.admin_fee
        for i in range(N_COINS):
            ideal_balance: uint256 = D1 * old_balances[i] / D0
            difference: uint256 = 0
            new_balance: uint256 = new_balances[i]
            if ideal_balance > new_balance:
                difference = ideal_balance - new_balance
            else:
                difference = new_balance - ideal_balance
            fees[i] = fee * difference / FEE_DENOMINATOR
            self.balances[i] = new_balance - (fees[i] * admin_fee / FEE_DENOMINATOR)
            new_balances[i] -= fees[i]
        D2 = self._get_D_mem(new_balances, amp)
        mint_amount = token_supply * (D2 - D0) / D0
    else:
        self.balances = new_balances
        mint_amount = D1  # Take the dust if there was any
    assert mint_amount >= _min_mint_amount, "Slippage screwed you"

    # Take coins from the sender
    for i in range(N_COINS):
        if _amounts[i] > 0:
            # "safeTransferFrom" which works for ERC20s which return bool or not
            _response: Bytes[32] = raw_call(
                self.coins[i],
                concat(
                    method_id("transferFrom(address,address,uint256)"),
                    convert(msg.sender, bytes32),
                    convert(self, bytes32),
                    convert(_amounts[i], bytes32),
                ),
                max_outsize=32,
            )
            if len(_response) > 0:
                assert convert(_response, bool)  # dev: failed transfer
            # end "safeTransferFrom"

    # Mint pool tokens
    CurveToken(lp_token).mint(msg.sender, mint_amount)

    log AddLiquidity(msg.sender, _amounts, fees, D1, token_supply + mint_amount)

    return mint_amount


@view
@internal
def _get_y(i: int128, j: int128, x: uint256, _xp: uint256[N_COINS]) -> uint256:
    """
    Calculate x[j] if one makes x[i] = x

    Done by solving quadratic equation iteratively.
    x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
    x_1**2 + b*x_1 = c

    x_1 = (x_1**2 + c) / (2*x_1 + b)
    """
    # x in the input is converted to the same price/precision

    assert i != j       # dev: same coin
    assert j >= 0       # dev: j below zero
    assert j < N_COINS  # dev: j above N_COINS

    # should be unreachable, but good for safety
    assert i >= 0
    assert i < N_COINS

    A: uint256 = self._A()
    D: uint256 = self._get_D(_xp, A)
    Ann: uint256 = A * N_COINS
    c: uint256 = D
    S: uint256 = 0
    _x: uint256 = 0
    y_prev: uint256 = 0

    for _i in range(N_COINS):
        if _i == i:
            _x = x
        elif _i != j:
            _x = _xp[_i]
        else:
            continue
        S += _x
        c = c * D / (_x * N_COINS)
    c = c * D * A_PRECISION / (Ann * N_COINS)
    b: uint256 = S + D * A_PRECISION / Ann  # - D
    y: uint256 = D
    for _i in range(255):
        y_prev = y
        y = (y*y + c) / (2 * y + b - D)
        # Equality with the precision of 1
        if y > y_prev:
            if y - y_prev <= 1:
                return y
        else:
            if y_prev - y <= 1:
                return y
    raise


@view
@external
def get_dy(i: int128, j: int128, _dx: uint256) -> uint256:
    xp: uint256[N_COINS] = self._xp()
    rates: uint256[N_COINS] = RATES

    x: uint256 = xp[i] + (_dx * rates[i] / PRECISION)
    y: uint256 = self._get_y(i, j, x, xp)
    dy: uint256 = xp[j] - y - 1
    fee: uint256 = self.fee * dy / FEE_DENOMINATOR
    return (dy - fee) * PRECISION / rates[j]


@external
@nonreentrant('lock')
def exchange(i: int128, j: int128, _dx: uint256, _min_dy: uint256) -> uint256:
    """
    @notice Perform an exchange between two coins
    @dev Index values can be found via the `coins` public getter method
    @param i Index value for the coin to send
    @param j Index valie of the coin to recieve
    @param _dx Amount of `i` being exchanged
    @param _min_dy Minimum amount of `j` to receive
    @return Actual amount of `j` received
    """
    assert not self.is_killed  # dev: is killed

    old_balances: uint256[N_COINS] = self.balances
    xp: uint256[N_COINS] = self._xp_mem(old_balances)

    rates: uint256[N_COINS] = RATES
    x: uint256 = xp[i] + _dx * rates[i] / PRECISION
    y: uint256 = self._get_y(i, j, x, xp)

    dy: uint256 = xp[j] - y - 1  # -1 just in case there were some rounding errors
    dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR

    # Convert all to real units
    dy = (dy - dy_fee) * PRECISION / rates[j]
    assert dy >= _min_dy, "Exchange resulted in fewer coins than expected"

    dy_admin_fee: uint256 = dy_fee * self.admin_fee / FEE_DENOMINATOR
    dy_admin_fee = dy_admin_fee * PRECISION / rates[j]

    # Change balances exactly in same way as we change actual ERC20 coin amounts
    self.balances[i] = old_balances[i] + _dx
    # When rounding errors happen, we undercharge admin fee in favor of LP
    self.balances[j] = old_balances[j] - dy - dy_admin_fee

    _response: Bytes[32] = raw_call(
        self.coins[i],
        concat(
            method_id("transferFrom(address,address,uint256)"),
            convert(msg.sender, bytes32),
            convert(self, bytes32),
            convert(_dx, bytes32),
        ),
        max_outsize=32,
    )
    if len(_response) > 0:
        assert convert(_response, bool)

    _response = raw_call(
        self.coins[j],
        concat(
            method_id("transfer(address,uint256)"),
            convert(msg.sender, bytes32),
            convert(dy, bytes32),
        ),
        max_outsize=32,
    )
    if len(_response) > 0:
        assert convert(_response, bool)

    log TokenExchange(msg.sender, i, _dx, j, dy)

    return dy


@external
@nonreentrant('lock')
def remove_liquidity(_amount: uint256, _min_amounts: uint256[N_COINS]) -> uint256[N_COINS]:
    """
    @notice Withdraw coins from the pool
    @dev Withdrawal amounts are based on current deposit ratios
    @param _amount Quantity of LP tokens to burn in the withdrawal
    @param _min_amounts Minimum amounts of underlying coins to receive
    @return List of amounts of coins that were withdrawn
    """
    lp_token: address = self.lp_token
    total_supply: uint256 = CurveToken(lp_token).totalSupply()
    amounts: uint256[N_COINS] = empty(uint256[N_COINS])

    for i in range(N_COINS):
        old_balance: uint256 = self.balances[i]
        value: uint256 = old_balance * _amount / total_supply
        assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected"
        self.balances[i] = old_balance - value
        amounts[i] = value
        _response: Bytes[32] = raw_call(
            self.coins[i],
            concat(
                method_id("transfer(address,uint256)"),
                convert(msg.sender, bytes32),
                convert(value, bytes32),
            ),
            max_outsize=32,
        )
        if len(_response) > 0:
            assert convert(_response, bool)

    CurveToken(lp_token).burnFrom(msg.sender, _amount)  # dev: insufficient funds

    log RemoveLiquidity(msg.sender, amounts, empty(uint256[N_COINS]), total_supply - _amount)

    return amounts


@external
@nonreentrant('lock')
def remove_liquidity_imbalance(_amounts: uint256[N_COINS], _max_burn_amount: uint256) -> uint256:
    """
    @notice Withdraw coins from the pool in an imbalanced amount
    @param _amounts List of amounts of underlying coins to withdraw
    @param _max_burn_amount Maximum amount of LP token to burn in the withdrawal
    @return Actual amount of the LP token burned in the withdrawal
    """
    assert not self.is_killed  # dev: is killed

    amp: uint256 = self._A()
    old_balances: uint256[N_COINS] = self.balances
    D0: uint256 = self._get_D_mem(old_balances, amp)
    new_balances: uint256[N_COINS] = old_balances
    for i in range(N_COINS):
        new_balances[i] -= _amounts[i]
    D1: uint256 = self._get_D_mem(new_balances, amp)

    fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
    admin_fee: uint256 = self.admin_fee
    fees: uint256[N_COINS] = empty(uint256[N_COINS])
    for i in range(N_COINS):
        new_balance: uint256 = new_balances[i]
        ideal_balance: uint256 = D1 * old_balances[i] / D0
        difference: uint256 = 0
        if ideal_balance > new_balance:
            difference = ideal_balance - new_balance
        else:
            difference = new_balance - ideal_balance
        fees[i] = fee * difference / FEE_DENOMINATOR
        self.balances[i] = new_balance - (fees[i] * admin_fee / FEE_DENOMINATOR)
        new_balances[i] = new_balance - fees[i]
    D2: uint256 = self._get_D_mem(new_balances, amp)

    lp_token: address = self.lp_token
    token_supply: uint256 = CurveToken(lp_token).totalSupply()
    token_amount: uint256 = (D0 - D2) * token_supply / D0
    assert token_amount != 0  # dev: zero tokens burned
    token_amount += 1  # In case of rounding errors - make it unfavorable for the "attacker"
    assert token_amount <= _max_burn_amount, "Slippage screwed you"

    CurveToken(lp_token).burnFrom(msg.sender, token_amount)  # dev: insufficient funds
    for i in range(N_COINS):
        if _amounts[i] != 0:
            _response: Bytes[32] = raw_call(
                self.coins[i],
                concat(
                    method_id("transfer(address,uint256)"),
                    convert(msg.sender, bytes32),
                    convert(_amounts[i], bytes32),
                ),
                max_outsize=32,
            )
            if len(_response) > 0:
                assert convert(_response, bool)

    log RemoveLiquidityImbalance(msg.sender, _amounts, fees, D1, token_supply - token_amount)

    return token_amount


@pure
@internal
def _get_y_D(A: uint256, i: int128, _xp: uint256[N_COINS], D: uint256) -> uint256:
    """
    Calculate x[i] if one reduces D from being calculated for xp to D

    Done by solving quadratic equation iteratively.
    x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
    x_1**2 + b*x_1 = c

    x_1 = (x_1**2 + c) / (2*x_1 + b)
    """
    # x in the input is converted to the same price/precision

    assert i >= 0  # dev: i below zero
    assert i < N_COINS  # dev: i above N_COINS

    Ann: uint256 = A * N_COINS
    c: uint256 = D
    S: uint256 = 0
    _x: uint256 = 0
    y_prev: uint256 = 0

    for _i in range(N_COINS):
        if _i != i:
            _x = _xp[_i]
        else:
            continue
        S += _x
        c = c * D / (_x * N_COINS)
    c = c * D * A_PRECISION / (Ann * N_COINS)
    b: uint256 = S + D * A_PRECISION / Ann
    y: uint256 = D

    for _i in range(255):
        y_prev = y
        y = (y*y + c) / (2 * y + b - D)
        # Equality with the precision of 1
        if y > y_prev:
            if y - y_prev <= 1:
                return y
        else:
            if y_prev - y <= 1:
                return y
    raise


@view
@internal
def _calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> (uint256, uint256, uint256):
    # First, need to calculate
    # * Get current D
    # * Solve Eqn against y_i for D - _token_amount
    amp: uint256 = self._A()
    xp: uint256[N_COINS] = self._xp()
    D0: uint256 = self._get_D(xp, amp)

    total_supply: uint256 = CurveToken(self.lp_token).totalSupply()
    D1: uint256 = D0 - _token_amount * D0 / total_supply
    new_y: uint256 = self._get_y_D(amp, i, xp, D1)
    xp_reduced: uint256[N_COINS] = xp
    fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
    for j in range(N_COINS):
        dx_expected: uint256 = 0
        if j == i:
            dx_expected = xp[j] * D1 / D0 - new_y
        else:
            dx_expected = xp[j] - xp[j] * D1 / D0
        xp_reduced[j] -= fee * dx_expected / FEE_DENOMINATOR

    dy: uint256 = xp_reduced[i] - self._get_y_D(amp, i, xp_reduced, D1)
    precisions: uint256[N_COINS] = PRECISION_MUL
    dy = (dy - 1) / precisions[i]  # Withdraw less to account for rounding errors
    dy_0: uint256 = (xp[i] - new_y) / precisions[i]  # w/o fees

    return dy, dy_0 - dy, total_supply


@view
@external
def calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> uint256:
    """
    @notice Calculate the amount received when withdrawing a single coin
    @param _token_amount Amount of LP tokens to burn in the withdrawal
    @param i Index value of the coin to withdraw
    @return Amount of coin received
    """
    return self._calc_withdraw_one_coin(_token_amount, i)[0]


@external
@nonreentrant('lock')
def remove_liquidity_one_coin(_token_amount: uint256, i: int128, _min_amount: uint256) -> uint256:
    """
    @notice Withdraw a single coin from the pool
    @param _token_amount Amount of LP tokens to burn in the withdrawal
    @param i Index value of the coin to withdraw
    @param _min_amount Minimum amount of coin to receive
    @return Amount of coin received
    """
    assert not self.is_killed  # dev: is killed

    dy: uint256 = 0
    dy_fee: uint256 = 0
    total_supply: uint256 = 0
    dy, dy_fee, total_supply = self._calc_withdraw_one_coin(_token_amount, i)
    assert dy >= _min_amount, "Not enough coins removed"

    self.balances[i] -= (dy + dy_fee * self.admin_fee / FEE_DENOMINATOR)
    CurveToken(self.lp_token).burnFrom(msg.sender, _token_amount)  # dev: insufficient funds

    _response: Bytes[32] = raw_call(
        self.coins[i],
        concat(
            method_id("transfer(address,uint256)"),
            convert(msg.sender, bytes32),
            convert(dy, bytes32),
        ),
        max_outsize=32,
    )
    if len(_response) > 0:
        assert convert(_response, bool)

    log RemoveLiquidityOne(msg.sender, _token_amount, dy, total_supply - _token_amount)

    return dy


### Admin functions ###
@external
def ramp_A(_future_A: uint256, _future_time: uint256):
    assert msg.sender == self.owner  # dev: only owner
    assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME
    assert _future_time >= block.timestamp + MIN_RAMP_TIME  # dev: insufficient time

    initial_A: uint256 = self._A()
    future_A_p: uint256 = _future_A * A_PRECISION

    assert _future_A > 0 and _future_A < MAX_A
    if future_A_p < initial_A:
        assert future_A_p * MAX_A_CHANGE >= initial_A
    else:
        assert future_A_p <= initial_A * MAX_A_CHANGE

    self.initial_A = initial_A
    self.future_A = future_A_p
    self.initial_A_time = block.timestamp
    self.future_A_time = _future_time

    log RampA(initial_A, future_A_p, block.timestamp, _future_time)


@external
def stop_ramp_A():
    assert msg.sender == self.owner  # dev: only owner

    current_A: uint256 = self._A()
    self.initial_A = current_A
    self.future_A = current_A
    self.initial_A_time = block.timestamp
    self.future_A_time = block.timestamp
    # now (block.timestamp < t1) is always False, so we return saved A

    log StopRampA(current_A, block.timestamp)


@external
def commit_new_fee(_new_fee: uint256, _new_admin_fee: uint256):
    assert msg.sender == self.owner  # dev: only owner
    assert self.admin_actions_deadline == 0  # dev: active action
    assert _new_fee <= MAX_FEE  # dev: fee exceeds maximum
    assert _new_admin_fee <= MAX_ADMIN_FEE  # dev: admin fee exceeds maximum

    deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
    self.admin_actions_deadline = deadline
    self.future_fee = _new_fee
    self.future_admin_fee = _new_admin_fee

    log CommitNewFee(deadline, _new_fee, _new_admin_fee)


@external
def apply_new_fee():
    assert msg.sender == self.owner  # dev: only owner
    assert block.timestamp >= self.admin_actions_deadline  # dev: insufficient time
    assert self.admin_actions_deadline != 0  # dev: no active action

    self.admin_actions_deadline = 0
    fee: uint256 = self.future_fee
    admin_fee: uint256 = self.future_admin_fee
    self.fee = fee
    self.admin_fee = admin_fee

    log NewFee(fee, admin_fee)


@external
def revert_new_parameters():
    assert msg.sender == self.owner  # dev: only owner

    self.admin_actions_deadline = 0


@external
def commit_transfer_ownership(_owner: address):
    assert msg.sender == self.owner  # dev: only owner
    assert self.transfer_ownership_deadline == 0  # dev: active transfer

    deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
    self.transfer_ownership_deadline = deadline
    self.future_owner = _owner

    log CommitNewAdmin(deadline, _owner)


@external
def apply_transfer_ownership():
    assert msg.sender == self.owner  # dev: only owner
    assert block.timestamp >= self.transfer_ownership_deadline  # dev: insufficient time
    assert self.transfer_ownership_deadline != 0  # dev: no active transfer

    self.transfer_ownership_deadline = 0
    owner: address = self.future_owner
    self.owner = owner

    log NewAdmin(owner)


@external
def revert_transfer_ownership():
    assert msg.sender == self.owner  # dev: only owner

    self.transfer_ownership_deadline = 0


@view
@external
def admin_balances(i: uint256) -> uint256:
    return ERC20(self.coins[i]).balanceOf(self) - self.balances[i]


@external
def withdraw_admin_fees():
    assert msg.sender == self.owner  # dev: only owner

    for i in range(N_COINS):
        coin: address = self.coins[i]
        value: uint256 = ERC20(coin).balanceOf(self) - self.balances[i]
        if value > 0:
            _response: Bytes[32] = raw_call(
                coin,
                concat(
                    method_id("transfer(address,uint256)"),
                    convert(msg.sender, bytes32),
                    convert(value, bytes32),
                ),
                max_outsize=32,
            )  # dev: failed transfer
            if len(_response) > 0:
                assert convert(_response, bool)


@external
def donate_admin_fees():
    assert msg.sender == self.owner  # dev: only owner
    for i in range(N_COINS):
        self.balances[i] = ERC20(self.coins[i]).balanceOf(self)


@external
def kill_me():
    assert msg.sender == self.owner  # dev: only owner
    assert self.kill_deadline > block.timestamp  # dev: deadline has passed
    self.is_killed = True


@external
def unkill_me():
    assert msg.sender == self.owner  # dev: only owner
    self.is_killed = False < 

Vyper_contract ABI

[{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"int128","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"int128","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityImbalance","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"CommitNewAdmin","inputs":[{"name":"deadline","type":"uint256","indexed":true},{"name":"admin","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"NewAdmin","inputs":[{"name":"admin","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"CommitNewFee","inputs":[{"name":"deadline","type":"uint256","indexed":true},{"name":"fee","type":"uint256","indexed":false},{"name":"admin_fee","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"NewFee","inputs":[{"name":"fee","type":"uint256","indexed":false},{"name":"admin_fee","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RampA","inputs":[{"name":"old_A","type":"uint256","indexed":false},{"name":"new_A","type":"uint256","indexed":false},{"name":"initial_time","type":"uint256","indexed":false},{"name":"future_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StopRampA","inputs":[{"name":"A","type":"uint256","indexed":false},{"name":"t","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_owner","type":"address"},{"name":"_coins","type":"address[2]"},{"name":"_pool_token","type":"address"},{"name":"_A","type":"uint256"},{"name":"_fee","type":"uint256"},{"name":"_admin_fee","type":"uint256"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":10058},{"stateMutability":"view","type":"function","name":"A_precise","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":10058},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":804988},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_is_deposit","type":"bool"}],"outputs":[{"name":"","type":"uint256"}],"gas":1593467},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":2546841},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1149036},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1307513},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[2]"}],"outputs":[{"name":"","type":"uint256[2]"}],"gas":169716},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_max_burn_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":2546560},{"stateMutability":"view","type":"function","name":"calc_withdraw_one_coin","inputs":[{"name":"_token_amount","type":"uint256"},{"name":"i","type":"int128"}],"outputs":[{"name":"","type":"uint256"}],"gas":989},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_token_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1620521},{"stateMutability":"nonpayable","type":"function","name":"ramp_A","inputs":[{"name":"_future_A","type":"uint256"},{"name":"_future_time","type":"uint256"}],"outputs":[],"gas":158394},{"stateMutability":"nonpayable","type":"function","name":"stop_ramp_A","inputs":[],"outputs":[],"gas":154617},{"stateMutability":"nonpayable","type":"function","name":"commit_new_fee","inputs":[{"name":"_new_fee","type":"uint256"},{"name":"_new_admin_fee","type":"uint256"}],"outputs":[],"gas":113298},{"stateMutability":"nonpayable","type":"function","name":"apply_new_fee","inputs":[],"outputs":[],"gas":103621},{"stateMutability":"nonpayable","type":"function","name":"revert_new_parameters","inputs":[],"outputs":[],"gas":22901},{"stateMutability":"nonpayable","type":"function","name":"commit_transfer_ownership","inputs":[{"name":"_owner","type":"address"}],"outputs":[],"gas":78536},{"stateMutability":"nonpayable","type":"function","name":"apply_transfer_ownership","inputs":[],"outputs":[],"gas":66804},{"stateMutability":"nonpayable","type":"function","name":"revert_transfer_ownership","inputs":[],"outputs":[],"gas":22991},{"stateMutability":"view","type":"function","name":"admin_balances","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":7769},{"stateMutability":"nonpayable","type":"function","name":"withdraw_admin_fees","inputs":[],"outputs":[],"gas":31496},{"stateMutability":"nonpayable","type":"function","name":"donate_admin_fees","inputs":[],"outputs":[],"gas":82442},{"stateMutability":"nonpayable","type":"function","name":"kill_me","inputs":[],"outputs":[],"gas":40304},{"stateMutability":"nonpayable","type":"function","name":"unkill_me","inputs":[],"outputs":[],"gas":23141},{"stateMutability":"view","type":"function","name":"coins","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}],"gas":3225},{"stateMutability":"view","type":"function","name":"balances","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":3255},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3240},{"stateMutability":"view","type":"function","name":"admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3270},{"stateMutability":"view","type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address"}],"gas":3300},{"stateMutability":"view","type":"function","name":"lp_token","inputs":[],"outputs":[{"name":"","type":"address"}],"gas":3330},{"stateMutability":"view","type":"function","name":"initial_A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3360},{"stateMutability":"view","type":"function","name":"future_A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3390},{"stateMutability":"view","type":"function","name":"initial_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3420},{"stateMutability":"view","type":"function","name":"future_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3450},{"stateMutability":"view","type":"function","name":"admin_actions_deadline","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3480},{"stateMutability":"view","type":"function","name":"transfer_ownership_deadline","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3510},{"stateMutability":"view","type":"function","name":"future_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3540},{"stateMutability":"view","type":"function","name":"future_admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3570},{"stateMutability":"view","type":"function","name":"future_owner","inputs":[],"outputs":[{"name":"","type":"address"}],"gas":3600}]
[{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"int128","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"int128","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityImbalance","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"CommitNewAdmin","inputs":[{"name":"deadline","type":"uint256","indexed":true},{"name":"admin","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"NewAdmin","inputs":[{"name":"admin","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"CommitNewFee","inputs":[{"name":"deadline","type":"uint256","indexed":true},{"name":"fee","type":"uint256","indexed":false},{"name":"admin_fee","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"NewFee","inputs":[{"name":"fee","type":"uint256","indexed":false},{"name":"admin_fee","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RampA","inputs":[{"name":"old_A","type":"uint256","indexed":false},{"name":"new_A","type":"uint256","indexed":false},{"name":"initial_time","type":"uint256","indexed":false},{"name":"future_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StopRampA","inputs":[{"name":"A","type":"uint256","indexed":false},{"name":"t","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_owner","type":"address"},{"name":"_coins","type":"address[2]"},{"name":"_pool_token","type":"address"},{"name":"_A","type":"uint256"},{"name":"_fee","type":"uint256"},{"name":"_admin_fee","type":"uint256"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":10058},{"stateMutability":"view","type":"function","name":"A_precise","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":10058},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":804988},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_is_deposit","type":"bool"}],"outputs":[{"name":"","type":"uint256"}],"gas":1593467},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":2546841},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1149036},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1307513},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[2]"}],"outputs":[{"name":"","type":"uint256[2]"}],"gas":169716},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_max_burn_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":2546560},{"stateMutability":"view","type":"function","name":"calc_withdraw_one_coin","inputs":[{"name":"_token_amount","type":"uint256"},{"name":"i","type":"int128"}],"outputs":[{"name":"","type":"uint256"}],"gas":989},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_token_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1620521},{"stateMutability":"nonpayable","type":"function","name":"ramp_A","inputs":[{"name":"_future_A","type":"uint256"},{"name":"_future_time","type":"uint256"}],"outputs":[],"gas":158394},{"stateMutability":"nonpayable","type":"function","name":"stop_ramp_A","inputs":[],"outputs":[],"gas":154617},{"stateMutability":"nonpayable","type":"function","name":"commit_new_fee","inputs":[{"name":"_new_fee","type":"uint256"},{"name":"_new_admin_fee","type":"uint256"}],"outputs":[],"gas":113298},{"stateMutability":"nonpayable","type":"function","name":"apply_new_fee","inputs":[],"outputs":[],"gas":103621},{"stateMutability":"nonpayable","type":"function","name":"revert_new_parameters","inputs":[],"outputs":[],"gas":22901},{"stateMutability":"nonpayable","type":"function","name":"commit_transfer_ownership","inputs":[{"name":"_owner","type":"address"}],"outputs":[],"gas":78536},{"stateMutability":"nonpayable","type":"function","name":"apply_transfer_ownership","inputs":[],"outputs":[],"gas":66804},{"stateMutability":"nonpayable","type":"function","name":"revert_transfer_ownership","inputs":[],"outputs":[],"gas":22991},{"stateMutability":"view","type":"function","name":"admin_balances","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":7769},{"stateMutability":"nonpayable","type":"function","name":"withdraw_admin_fees","inputs":[],"outputs":[],"gas":31496},{"stateMutability":"nonpayable","type":"function","name":"donate_admin_fees","inputs":[],"outputs":[],"gas":82442},{"stateMutability":"nonpayable","type":"function","name":"kill_me","inputs":[],"outputs":[],"gas":40304},{"stateMutability":"nonpayable","type":"function","name":"unkill_me","inputs":[],"outputs":[],"gas":23141},{"stateMutability":"view","type":"function","name":"coins","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}],"gas":3225},{"stateMutability":"view","type":"function","name":"balances","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":3255},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3240},{"stateMutability":"view","type":"function","name":"admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3270},{"stateMutability":"view","type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address"}],"gas":3300},{"stateMutability":"view","type":"function","name":"lp_token","inputs":[],"outputs":[{"name":"","type":"address"}],"gas":3330},{"stateMutability":"view","type":"function","name":"initial_A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3360},{"stateMutability":"view","type":"function","name":"future_A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3390},{"stateMutability":"view","type":"function","name":"initial_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3420},{"stateMutability":"view","type":"function","name":"future_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3450},{"stateMutability":"view","type":"function","name":"admin_actions_deadline","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3480},{"stateMutability":"view","type":"function","name":"transfer_ownership_deadline","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3510},{"stateMutability":"view","type":"function","name":"future_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3540},{"stateMutability":"view","type":"function","name":"future_admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3570},{"stateMutability":"view","type":"function","name":"future_owner","inputs":[],"outputs":[{"name":"","type":"address"}],"gas":3600}]

Vyper_contract Bytecode

60206136ee6080396080518060a01c6136e95760e05260206136ee016020816080396080518060a01c6136e957610100526020602082016080396080518060a01c6136e9576101205250602060606136ee016080396080518060a01c6136e9576101405261016060006002818352015b60006101006101605160028110156136e9576020020151146136e957815160010180835281141561006f5750506101005160015561012051600255602060806136ee0160803960805160648082028215828483041417156136e95790509050600955602060806136ee0160803960805160648082028215828483041417156136e95790509050600a55602060a06136ee01608039608051600555602060c06136ee0160803960805160065560e05160075542624f1a0081818301106136e95780820190509050601355610140516008556136d156600436101561000d57612698565b60046000601c37600051346135885763f446c1d0811861004a5761003261016061269e565b61016051606480820490509050610180526020610180f35b6376a2f0f0811861006f5761006061016061269e565b61016051610180526020610180f35b63bb7b8b80811861014a576100856102606127d9565b61026080516102e052806020015161030052506100a36102a061269e565b6102a051610320526102e05160e052610300516101005261032051610120526100cd6102c0612935565b6102c051610240526318160ddd610280526020610280600461029c6008545afa6100fc573d600060003e3d6000fd5b601f3d111561358857610280516102605261024051670de0b6b3a764000080820282158284830414171561358857905090506102605180801561358857820490509050610280526020610280f35b63ed8e84f38118610324576044358060011c613588576103605261016f6103a061269e565b6103a051610380526003546103a0526004546103c0526103a051610240526103c0516102605261038051610280526101a8610400612ba2565b610400516103e05261040060006002818352015b610360516101fb576103a06104005160028110156135885760200201805160206104005102600401358082106135885780820390509050815250610230565b6103a0610400516002811015613588576020020180516020610400510260040135818183011061358857808201905090508152505b81516001018083528114156101bc5750506103a051610240526103c051610260526103805161028052610264610420612ba2565b61042051610400526318160ddd610440526020610440600461045c6008545afa610293573d600060003e3d6000fd5b601f3d1115613588576104405161042052600061044052610360516102d1576103e051610400518082106135885780820390509050610440526102ec565b610400516103e0518082106135885780820390509050610440525b610440516104205180820282158284830414171561358857905090506103e05180801561358857820490509050610460526020610460f35b630b4c7e4d8118610983576000546135885760016000556012546135885761034d61038061269e565b6103805161036052600354610380526004546103a05261038051610240526103a0516102605261036051610280526103866103e0612ba2565b6103e0516103c0526008546103e0526318160ddd610420526020610420600461043c6103e0515afa6103bd573d600060003e3d6000fd5b601f3d111561358857610420516104005261038051610420526103a0516104405261046060006002818352015b6104005161040657600060206104605102600401351115613588575b6104206104605160028110156135885760200201805160206104605102600401358181830110613588578082019050905081525081516001018083528114156103ea57505061042051610240526104405161026052610360516102805261046e610480612ba2565b61048051610460526103c0516104605111156135885761046051610480526060366104a037600061040051116104b9576104205160035561044051600455610460516104e0526106f4565b60055460028082028215828483041417156135885790509050600480820490509050610500526006546105205261054060006002818352015b6104605161038061054051600281101561358857602002015180820282158284830414171561358857905090506103c05180801561358857820490509050610560526000610580526104206105405160028110156135885760200201516105a0526105a051610560511161057f576105a0516105605180821061358857808203905090506105805261059a565b610560516105a0518082106135885780820390509050610580525b610500516105805180820282158284830414171561358857905090506402540be400808204905090506104a06105405160028110156135885760200201526105a0516104a06105405160028110156135885760200201516105205180820282158284830414171561358857905090506402540be4008082049050905080821061358857808203905090506001610540516002811015613588570260030155610420610540516002811015613588576020020180516104a0610540516002811015613588576020020151808210613588578082039050905081525081516001018083528114156104f25750506104205161024052610440516102605261036051610280526106a8610540612ba2565b610540516104805261040051610480516103c051808210613588578082039050905080820282158284830414171561358857905090506103c051808015613588578204905090506104e0525b6044356104e0511015610778576014610500527f536c697070616765207363726577656420796f750000000000000000000000006105205261050050610500518061052001818260206001820306601f82010390500336823750506308c379a06104c05260206104e0526105005160206001820306601f82010390506044016104dcfd5b61050060006002818352015b6000602061050051026004013511156108b05760006004610560527f23b872dd00000000000000000000000000000000000000000000000000000000610580526105606004806020846105a00101826020850160045afa505080518201915050336020826105a0010152602081019050306020826105a001015260208101905060206105005102600401356020826105a0010152602081019050806105a0526105a0505060206106606105a0516105c0600060016105005160028110156135885702600101545af161085b573d600060003e3d6000fd5b61064060203d80821161086e5781610870565b805b905090508152805160200180610520828460045afa9050505060006105205111156108b057610540516105205181816020036008021c9050905015613588575b81516001018083528114156107845750506340c10f196105005233610520526104e051610540526020610500604461051c60006103e0515af16108f8573d600060003e3d6000fd5b601f3d11156135885761050050337f26f55a85081d24974e85c6c00045d0f0453991e95873f52bff0d21af4079a76860043561050052602435610520526104a051610540526104c051610560526104605161058052610400516104e051818183011061358857808201905090506105a05260c0610500a26104e0516105005260206105006000600055f35b635e0d443f8118610b5a576004358060801d81607f1d1861358857610420526024358060801d81607f1d1861358857610440526109c16104a06127d9565b6104a080516104605280602001516104805250670de0b6b3a76400006104a0526c0c9f2c9cd04674edea400000006104c0526104606104205160028110156135885760200201516044356104a06104205160028110156135885760200201518082028215828483041417156135885790509050670de0b6b3a764000080820490509050818183011061358857808201905090506104e052610420516102405261044051610260526104e05161028052610460516102a052610480516102c052610a8b610520612c02565b610520516105005261046061044051600281101561358857602002015161050051808210613588578082039050905060018082106135885780820390509050610520526005546105205180820282158284830414171561358857905090506402540be400808204905090506105405261052051610540518082106135885780820390509050670de0b6b3a764000080820282158284830414171561358857905090506104a061044051600281101561358857602002015180801561358857820490509050610560526020610560f35b633df02124811861116d576004358060801d81607f1d1861358857610420526024358060801d81607f1d1861358857610440526000546135885760016000556012546135885760035461046052600454610480526104605160e0526104805161010052610bc86104e0612885565b6104e080516104a05280602001516104c05250670de0b6b3a76400006104e0526c0c9f2c9cd04674edea40000000610500526104a06104205160028110156135885760200201516044356104e06104205160028110156135885760200201518082028215828483041417156135885790509050670de0b6b3a76400008082049050905081818301106135885780820190509050610520526104205161024052610440516102605261052051610280526104a0516102a0526104c0516102c052610c92610560612c02565b61056051610540526104a061044051600281101561358857602002015161054051808210613588578082039050905060018082106135885780820390509050610560526105605160055480820282158284830414171561358857905090506402540be400808204905090506105805261056051610580518082106135885780820390509050670de0b6b3a764000080820282158284830414171561358857905090506104e06104405160028110156135885760200201518080156135885782049050905061056052606435610560511015610e0357602e6105a0527f45786368616e676520726573756c74656420696e20666577657220636f696e736105c0527f207468616e2065787065637465640000000000000000000000000000000000006105e0526105a0506105a051806105c001818260206001820306601f82010390500336823750506308c379a0610560526020610580526105a05160206001820306601f820103905060440161057cfd5b6105805160065480820282158284830414171561358857905090506402540be400808204905090506105a0526105a051670de0b6b3a764000080820282158284830414171561358857905090506104e0610440516002811015613588576020020151808015613588578204905090506105a0526104606104205160028110156135885760200201516044358181830110613588578082019050905060016104205160028110156135885702600301556104606104405160028110156135885760200201516105605180821061358857808203905090506105a0518082106135885780820390509050600161044051600281101561358857026003015560006004610600527f23b872dd00000000000000000000000000000000000000000000000000000000610620526106006004806020846106400101826020850160045afa505080518201915050336020826106400101526020810190503060208261064001015260208101905060443560208261064001015260208101905080610640526106405050602061070061064051610660600060016104205160028110156135885702600101545af1610fbb573d600060003e3d6000fd5b6106e060203d808211610fce5781610fd0565b805b9050905081528051602001806105c0828460045afa9050505060006105c0511115611010576105e0516105c05181816020036008021c9050905015613588575b60006004610600527fa9059cbb00000000000000000000000000000000000000000000000000000000610620526106006004806020846106400101826020850160045afa50508051820191505033602082610640010152602081019050610560516020826106400101526020810190508061064052610640505060206106e061064051610660600060016104405160028110156135885702600101545af16110bd573d600060003e3d6000fd5b6106c060203d8082116110d057816110d2565b805b9050905081528051602001806105c0828460045afa9050505060006105c0511115611112576105e0516105c05181816020036008021c9050905015613588575b337f8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd97140610420516106005260443561062052610440516106405261056051610660526080610600a2610560516106005260206106006000600055f35b635b36389c81186114da5760005461358857600160005560085460e0526318160ddd610120526020610120600461013c60e0515afa6111b1573d600060003e3d6000fd5b601f3d11156135885761012051610100526040366101203761016060006002818352015b60016101605160028110156135885702600301546101805261018051600435808202821582848304141715613588579050905061010051808015613588578204905090506101a05260206101605102602401356101a05110156112ce5760306101c0527f5769746864726177616c20726573756c74656420696e20666577657220636f696101e0527f6e73207468616e20657870656374656400000000000000000000000000000000610200526101c0506101c051806101e001818260206001820306601f82010390500336823750506308c379a06101805260206101a0526101c05160206001820306601f820103905060440161019cfd5b610180516101a051808210613588578082039050905060016101605160028110156135885702600301556101a05161012061016051600281101561358857602002015260006004610200527fa9059cbb00000000000000000000000000000000000000000000000000000000610220526102006004806020846102400101826020850160045afa505080518201915050336020826102400101526020810190506101a0516020826102400101526020810190508061024052610240505060206102e061024051610260600060016101605160028110156135885702600101545af16113be573d600060003e3d6000fd5b6102c060203d8082116113d157816113d3565b805b9050905081528051602001806101c0828460045afa9050505060006101c0511115611413576101e0516101c05181816020036008021c9050905015613588575b81516001018083528114156111d55750506379cc67906101605233610180526004356101a0526020610160604461017c600060e0515af1611459573d600060003e3d6000fd5b601f3d11156135885761016050337f7c363854ccf79623411f8995b362bce5eddff18c927edc6f5dbbb5e05819a82c610120516101605261014051610180526040366101a0376101005160043580821061358857808203905090506101e05260a0610160a26101205161016052610140516101805260406101606000600055f35b63e31032738118611af0576000546135885760016000556012546135885761150361038061269e565b6103805161036052600354610380526004546103a05261038051610240526103a05161026052610360516102805261153c6103e0612ba2565b6103e0516103c052610380516103e0526103a0516104005261042060006002818352015b6103e0610420516002811015613588576020020180516020610420510260040135808210613588578082039050905081525081516001018083528114156115605750506103e05161024052610400516102605261036051610280526115c6610440612ba2565b6104405161042052600554600280820282158284830414171561358857905090506004808204905090506104405260065461046052604036610480376104c060006002818352015b6103e06104c05160028110156135885760200201516104e052610420516103806104c051600281101561358857602002015180820282158284830414171561358857905090506103c05180801561358857820490509050610500526000610520526104e051610500511161169b576104e051610500518082106135885780820390509050610520526116b6565b610500516104e0518082106135885780820390509050610520525b610440516105205180820282158284830414171561358857905090506402540be400808204905090506104806104c05160028110156135885760200201526104e0516104806104c05160028110156135885760200201516104605180820282158284830414171561358857905090506402540be40080820490509050808210613588578082039050905060016104c05160028110156135885702600301556104e0516104806104c051600281101561358857602002015180821061358857808203905090506103e06104c0516002811015613588576020020152815160010180835281141561160e5750506103e05161024052610400516102605261036051610280526117c46104e0612ba2565b6104e0516104c0526008546104e0526318160ddd610520526020610520600461053c6104e0515afa6117fb573d600060003e3d6000fd5b601f3d11156135885761052051610500526103c0516104c05180821061358857808203905090506105005180820282158284830414171561358857905090506103c0518080156135885782049050905061052052600061052051146135885761052080516001818183011061358857808201905090508152506044356105205111156118f8576014610540527f536c697070616765207363726577656420796f750000000000000000000000006105605261054050610540518061056001818260206001820306601f82010390500336823750506308c379a0610500526020610520526105405160206001820306601f820103905060440161051cfd5b6379cc679061054052336105605261052051610580526020610540604461055c60006104e0515af161192f573d600060003e3d6000fd5b601f3d1115613588576105405061054060006002818352015b6000602061054051026004013514611a6357600060046105a0527fa9059cbb000000000000000000000000000000000000000000000000000000006105c0526105a06004806020846105e00101826020850160045afa505080518201915050336020826105e001015260208101905060206105405102600401356020826105e0010152602081019050806105e0526105e0505060206106806105e051610600600060016105405160028110156135885702600101545af1611a0e573d600060003e3d6000fd5b61066060203d808211611a215781611a23565b805b905090508152805160200180610560828460045afa905050506000610560511115611a6357610580516105605181816020036008021c9050905015613588575b8151600101808352811415611948575050337f2b5508378d7e19e0d5fa338419034731416c4f5b219a10379956f764317fd47e600435610540526024356105605261048051610580526104a0516105a052610420516105c052610500516105205180821061358857808203905090506105e05260c0610540a2610520516105405260206105406000600055f35b63cc2b27d78118611b38576024358060801d81607f1d18613588576104805260043561028052610480516102a052611b296104a06131e2565b6104a051610500526020610500f35b631a4d01d28118611e35576024358060801d81607f1d186135885761048052600054613588576001600055601254613588576060366104a03760043561028052610480516102a052611b8b6105006131e2565b61050080516104a05260208101516104c05260408101516104e052506044356104a0511015611c2b576018610500527f4e6f7420656e6f75676820636f696e732072656d6f76656400000000000000006105205261050050610500518061052001818260206001820306601f82010390500336823750506308c379a06104c05260206104e0526105005160206001820306601f82010390506044016104dcfd5b6001610480516002811015613588570260030180546104a0516104c05160065480820282158284830414171561358857905090506402540be400808204905090508181830110613588578082019050905080821061358857808203905090508155506379cc6790610500523361052052600435610540526020610500604461051c60006008545af1611cc2573d600060003e3d6000fd5b601f3d1115613588576105005060006004610540527fa9059cbb00000000000000000000000000000000000000000000000000000000610560526105406004806020846105800101826020850160045afa505080518201915050336020826105800101526020810190506104a051602082610580010152602081019050806105805261058050506020610620610580516105a0600060016104805160028110156135885702600101545af1611d7c573d600060003e3d6000fd5b61060060203d808211611d8f5781611d91565b805b905090508152805160200180610500828460045afa905050506000610500511115611dd157610520516105005181816020036008021c9050905015613588575b337f5ad056f2e28a8cec232015406b843668c1e36cda598127ec3b8c59b8c72773a0600435610540526104a051610560526104e0516004358082106135885780820390509050610580526060610540a26104a0516105405260206105406000600055f35b633c157e648118611f8857600754331861358857600b5462015180818183011061358857808201905090504210613588574262015180818183011061358857808201905090506024351061358857611e8e61018061269e565b61018051610160526004356064808202821582848304141715613588579050905061018052600060043511611ec4576000611ecd565b620f4240600435105b1561358857610160516101805110611f075761016051600a8082028215828483041417156135885790509050610180511161358857611f2b565b6101605161018051600a808202821582848304141715613588579050905010613588575b6101605160095561018051600a5542600b55602435600c557fa2b71ec6df949300b59aab36b55e189697b750119dd349fcfa8c0f779e83c254610160516101a052610180516101c052426101e0526024356102005260806101a0a1005b63551a65888118611ffb57600754331861358857611fa761018061269e565b61018051610160526101605160095561016051600a5542600b5542600c557f46e22fb3709ad289f62ce63d469248536dbc78d82b84a3d7e74ad606dc2019386101605161018052426101a0526040610180a1005b635b5a1467811861209657600754331861358857600d546135885764012a05f20060043511613588576402540be4006024351161358857426203f4808181830110613588578082019050905060e05260e051600d55600435600f5560243560105560e0517f351fc5da2fbf480f2225debf3664a4bc90fa9923743aad58b4603f648e931fe060043561010052602435610120526040610100a2005b634f12fe97811861211457600754331861358857600d544210613588576000600d5414613588576000600d55600f5460e0526010546101005260e051600555610100516006557fbe12859b636aed607d5230b2cc2711f68d70e51060e6cca1f575ef5d2fcc95d160e0516101205261010051610140526040610120a1005b63226840fb811861212f576007543318613588576000600d55005b636b441a4081186121ae576004358060a01c6135885760e052600754331861358857600e5461358857426203f480818183011061358857808201905090506101005261010051600e5560e05160115560e051610100517f181aa3aa17d4cbf99265dd4443eba009433d3cde79d60164fde1d1a192beb9356000610120a3005b636a1c05ae811861221257600754331861358857600e544210613588576000600e5414613588576000600e5560115460e05260e05160075560e0517f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c6000610100a2005b6386fbf193811861222d576007543318613588576000600e55005b63e2e7d26481186122a9576370a0823160e0523061010052602060e0602460fc600160043560028110156135885702600101545afa612271573d600060003e3d6000fd5b601f3d11156135885760e051600160043560028110156135885702600301548082106135885780820390509050610120526020610120f35b6330c54085811861244f5760075433186135885760e060006002818352015b600160e0516002811015613588570260010154610100526370a082316101405230610160526020610140602461015c610100515afa61230c573d600060003e3d6000fd5b601f3d11156135885761014051600160e0516002811015613588570260030154808210613588578082039050905061012052600061012051111561243c5760006004610180527fa9059cbb000000000000000000000000000000000000000000000000000000006101a0526101806004806020846101c00101826020850160045afa505080518201915050336020826101c0010152602081019050610120516020826101c0010152602081019050806101c0526101c0505060206102606101c0516101e06000610100515af16123e7573d600060003e3d6000fd5b61024060203d8082116123fa57816123fc565b805b905090508152805160200180610140828460045afa90505050600061014051111561243c57610160516101405181816020036008021c9050905015613588575b81516001018083528114156122c8575050005b63524c390181186124dd5760075433186135885760e060006002818352015b6370a082316101005230610120526020610100602461011c600160e05160028110156135885702600101545afa6124aa573d600060003e3d6000fd5b601f3d11156135885761010051600160e0516002811015613588570260030155815160010180835281141561246e575050005b63e3698853811861250257600754331861358857426013541115613588576001601255005b633046f972811861251d576007543318613588576000601255005b63c66106578118612544576001600435600281101561358857026001015460e052602060e0f35b634903b0d1811861256b576001600435600281101561358857026003015460e052602060e0f35b63ddca3f4381186125825760055460e052602060e0f35b63fee3f7f981186125995760065460e052602060e0f35b638da5cb5b81186125b05760075460e052602060e0f35b6382c6306681186125c75760085460e052602060e0f35b635409491a81186125de5760095460e052602060e0f35b63b4b577ad81186125f557600a5460e052602060e0f35b632081066c811861260c57600b5460e052602060e0f35b6314052288811861262357600c5460e052602060e0f35b63405e28f8811861263a57600d5460e052602060e0f35b63e0a0b586811861265157600e5460e052602060e0f35b6358680d0b811861266857600f5460e052602060e0f35b63e3824462811861267f5760105460e052602060e0f35b631ec0cdc181186126965760115460e052602060e0f35b505b60006000fd5b600c5460e052600a546101005260e05142106126c457610100518152506127d7566127d7565b60095461012052600b546101405261012051610100511161275c57610120516101205161010051808210613588578082039050905042610140518082106135885780820390509050808202821582848304141715613588579050905060e0516101405180821061358857808203905090508080156135885782049050905080821061358857808203905090508152506127d7566127d7565b610120516101005161012051808210613588578082039050905042610140518082106135885780820390509050808202821582848304141715613588579050905060e05161014051808210613588578082039050905080801561358857820490509050818183011061358857808201905090508152506127d7565b565b670de0b6b3a764000060e0526c0c9f2c9cd04674edea400000006101005261012060006002818352015b60e061012051600281101561358857602002015160016101205160028110156135885702600301548082028215828483041417156135885790509050670de0b6b3a76400008082049050905060e0610120516002811015613588576020020152815160010180835281141561280357505060e051815261010051816020015250565b670de0b6b3a7640000610120526c0c9f2c9cd04674edea400000006101405261016060006002818352015b61012061016051600281101561358857602002015160e06101605160028110156135885760200201518082028215828483041417156135885790509050670de0b6b3a76400008082049050905061012061016051600281101561358857602002015281516001018083528114156128b057505061012051815261014051816020015250565b604036610140376101a060006002818352015b60206101a0510260e001516101805261014080516101805181818301106135885780820190509050815250815160010180835281141561294857505061014051612996576000815250612ba0565b610140516101805261012051600280820282158284830414171561358857905090506101a0526101c0600060ff818352015b610180516101e05261022060006002818352015b6020610220510260e00151610200526101e0516101805180820282158284830414171561358857905090506102005160028082028215828483041417156135885790509050808015613588578204905090506101e05281516001018083528114156129dc57505061018051610160526101a0516101405180820282158284830414171561358857905090506064808204905090506101e05160028082028215828483041417156135885790509050818183011061358857808201905090506101805180820282158284830414171561358857905090506101a0516064808210613588578082039050905061018051808202821582848304141715613588579050905060648082049050905060036101e0518082028215828483041417156135885790509050818183011061358857808201905090508080156135885782049050905061018052610160516101805111612b5e5760016101605161018051808210613588578082039050905011612b8957505061018051815250612ba056612b89565b60016101805161016051808210613588578082039050905011612b8957505061018051815250612ba0565b81516001018083528114156129c857505060006000fd5b565b6102405160e0526102605161010052612bbc6102a0612885565b6102a08051610300528060200151610320525061028051610340526103005160e05261032051610100526103405161012052612bf96102e0612935565b6102e051815250565b6102605161024051146135885760006102605112613588576002610260511215613588576000610240511261358857600261024051121561358857612c4861030061269e565b610300516102e0526102a05160e0526102c051610100526102e05161012052612c72610320612935565b61032051610300526102e05160028082028215828483041417156135885790509050610320526103005161034052606036610360376103c060006002818352015b610240516103c05118612ccd576102805161038052612cfe565b610260516103c0511415612ce457612d6256612cfe565b6102a06103c0516002811015613588576020020151610380525b6103608051610380518181830110613588578082019050905081525061034051610300518082028215828483041417156135885790509050610380516002808202821582848304141715613588579050905080801561358857820490509050610340525b8151600101808352811415612cb35750506103405161030051808202821582848304141715613588579050905060648082028215828483041417156135885790509050610320516002808202821582848304141715613588579050905080801561358857820490509050610340526103605161030051606480820282158284830414171561358857905090506103205180801561358857820490509050818183011061358857808201905090506103c052610300516103e052610400600060ff818352015b6103e0516103a0526103e0516103e0518082028215828483041417156135885790509050610340518181830110613588578082019050905060026103e05180820282158284830414171561358857905090506103c05181818301106135885780820190509050610300518082106135885780820390509050808015613588578204905090506103e0526103a0516103e05111612eec5760016103a0516103e051808210613588578082039050905011612f175750506103e051815250612f2e56612f17565b60016103e0516103a051808210613588578082039050905011612f175750506103e051815250612f2e565b8151600101808352811415612e2757505060006000fd5b565b600061010051126135885760026101005112156135885760e0516002808202821582848304141715613588579050905061018052610160516101a0526060366101c03761022060006002818352015b61010051610220511415612f965761301456612fb0565b6101206102205160028110156135885760200201516101e0525b6101c080516101e051818183011061358857808201905090508152506101a0516101605180820282158284830414171561358857905090506101e05160028082028215828483041417156135885790509050808015613588578204905090506101a0525b8151600101808352811415612f7f5750506101a051610160518082028215828483041417156135885790509050606480820282158284830414171561358857905090506101805160028082028215828483041417156135885790509050808015613588578204905090506101a0526101c0516101605160648082028215828483041417156135885790509050610180518080156135885782049050905081818301106135885780820190509050610220526101605161024052610260600060ff818352015b6102405161020052610240516102405180820282158284830414171561358857905090506101a0518181830110613588578082019050905060026102405180820282158284830414171561358857905090506102205181818301106135885780820190509050610160518082106135885780820390509050808015613588578204905090506102405261020051610240511161319e57600161020051610240518082106135885780820390509050116131c9575050610240518152506131e0566131c9565b600161024051610200518082106135885780820390509050116131c9575050610240518152506131e0565b81516001018083528114156130d957505060006000fd5b565b6131ed6102e061269e565b6102e0516102c0526132006103206127d9565b61032080516102e052806020015161030052506102e05160e05261030051610100526102c05161012052613235610340612935565b61034051610320526318160ddd610360526020610360600461037c6008545afa613264573d600060003e3d6000fd5b601f3d1115613588576103605161034052610320516102805161032051808202821582848304141715613588579050905061034051808015613588578204905090508082106135885780820390509050610360526102c05160e0526102a051610100526102e05161012052610300516101405261036051610160526132ea6103a0612f30565b6103a051610380526102e0516103a052610300516103c052600554600280820282158284830414171561358857905090506004808204905090506103e05261040060006002818352015b6000610420526102a05161040051186133a0576102e0610400516002811015613588576020020151610360518082028215828483041417156135885790509050610320518080156135885782049050905061038051808210613588578082039050905061042052613406565b6102e06104005160028110156135885760200201516102e061040051600281101561358857602002015161036051808202821582848304141715613588579050905061032051808015613588578204905090508082106135885780820390509050610420525b6103a0610400516002811015613588576020020180516103e0516104205180820282158284830414171561358857905090506402540be40080820490509050808210613588578082039050905081525081516001018083528114156133345750506103a06102a05160028110156135885760200201516102c05160e0526102a051610100526103a051610120526103c0516101405261036051610160526134ae610420612f30565b6104205180821061358857808203905090506104005260016104205264e8d4a510006104405261040051600180821061358857808203905090506104206102a051600281101561358857602002015180801561358857820490509050610400526102e06102a05160028110156135885760200201516103805180821061358857808203905090506104206102a0516002811015613588576020020151808015613588578204905090506104605261040051815261046051610400518082106135885780820390509050602082015261034051604082015250565b600080fd5b6101446136d1036101446000396101446136d1036000f35b600080fd000000000000000000000000ecb456ea5365865ebab8a2661b0c503410e9b347000000000000000000000000853d955acef822db058eb8505911ed77f175b99e000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000ecebb5b9ebc11025493f9339f53d1ffb11e2882500000000000000000000000000000000000000000000000000000000000005dc0000000000000000000000000000000000000000000000000000000017d78400000000000000000000000000000000000000000000000000000000012a05f200
60206136ee6080396080518060a01c6136e95760e05260206136ee016020816080396080518060a01c6136e957610100526020602082016080396080518060a01c6136e9576101205250602060606136ee016080396080518060a01c6136e9576101405261016060006002818352015b60006101006101605160028110156136e9576020020151146136e957815160010180835281141561006f5750506101005160015561012051600255602060806136ee0160803960805160648082028215828483041417156136e95790509050600955602060806136ee0160803960805160648082028215828483041417156136e95790509050600a55602060a06136ee01608039608051600555602060c06136ee0160803960805160065560e05160075542624f1a0081818301106136e95780820190509050601355610140516008556136d156600436101561000d57612698565b60046000601c37600051346135885763f446c1d0811861004a5761003261016061269e565b61016051606480820490509050610180526020610180f35b6376a2f0f0811861006f5761006061016061269e565b61016051610180526020610180f35b63bb7b8b80811861014a576100856102606127d9565b61026080516102e052806020015161030052506100a36102a061269e565b6102a051610320526102e05160e052610300516101005261032051610120526100cd6102c0612935565b6102c051610240526318160ddd610280526020610280600461029c6008545afa6100fc573d600060003e3d6000fd5b601f3d111561358857610280516102605261024051670de0b6b3a764000080820282158284830414171561358857905090506102605180801561358857820490509050610280526020610280f35b63ed8e84f38118610324576044358060011c613588576103605261016f6103a061269e565b6103a051610380526003546103a0526004546103c0526103a051610240526103c0516102605261038051610280526101a8610400612ba2565b610400516103e05261040060006002818352015b610360516101fb576103a06104005160028110156135885760200201805160206104005102600401358082106135885780820390509050815250610230565b6103a0610400516002811015613588576020020180516020610400510260040135818183011061358857808201905090508152505b81516001018083528114156101bc5750506103a051610240526103c051610260526103805161028052610264610420612ba2565b61042051610400526318160ddd610440526020610440600461045c6008545afa610293573d600060003e3d6000fd5b601f3d1115613588576104405161042052600061044052610360516102d1576103e051610400518082106135885780820390509050610440526102ec565b610400516103e0518082106135885780820390509050610440525b610440516104205180820282158284830414171561358857905090506103e05180801561358857820490509050610460526020610460f35b630b4c7e4d8118610983576000546135885760016000556012546135885761034d61038061269e565b6103805161036052600354610380526004546103a05261038051610240526103a0516102605261036051610280526103866103e0612ba2565b6103e0516103c0526008546103e0526318160ddd610420526020610420600461043c6103e0515afa6103bd573d600060003e3d6000fd5b601f3d111561358857610420516104005261038051610420526103a0516104405261046060006002818352015b6104005161040657600060206104605102600401351115613588575b6104206104605160028110156135885760200201805160206104605102600401358181830110613588578082019050905081525081516001018083528114156103ea57505061042051610240526104405161026052610360516102805261046e610480612ba2565b61048051610460526103c0516104605111156135885761046051610480526060366104a037600061040051116104b9576104205160035561044051600455610460516104e0526106f4565b60055460028082028215828483041417156135885790509050600480820490509050610500526006546105205261054060006002818352015b6104605161038061054051600281101561358857602002015180820282158284830414171561358857905090506103c05180801561358857820490509050610560526000610580526104206105405160028110156135885760200201516105a0526105a051610560511161057f576105a0516105605180821061358857808203905090506105805261059a565b610560516105a0518082106135885780820390509050610580525b610500516105805180820282158284830414171561358857905090506402540be400808204905090506104a06105405160028110156135885760200201526105a0516104a06105405160028110156135885760200201516105205180820282158284830414171561358857905090506402540be4008082049050905080821061358857808203905090506001610540516002811015613588570260030155610420610540516002811015613588576020020180516104a0610540516002811015613588576020020151808210613588578082039050905081525081516001018083528114156104f25750506104205161024052610440516102605261036051610280526106a8610540612ba2565b610540516104805261040051610480516103c051808210613588578082039050905080820282158284830414171561358857905090506103c051808015613588578204905090506104e0525b6044356104e0511015610778576014610500527f536c697070616765207363726577656420796f750000000000000000000000006105205261050050610500518061052001818260206001820306601f82010390500336823750506308c379a06104c05260206104e0526105005160206001820306601f82010390506044016104dcfd5b61050060006002818352015b6000602061050051026004013511156108b05760006004610560527f23b872dd00000000000000000000000000000000000000000000000000000000610580526105606004806020846105a00101826020850160045afa505080518201915050336020826105a0010152602081019050306020826105a001015260208101905060206105005102600401356020826105a0010152602081019050806105a0526105a0505060206106606105a0516105c0600060016105005160028110156135885702600101545af161085b573d600060003e3d6000fd5b61064060203d80821161086e5781610870565b805b905090508152805160200180610520828460045afa9050505060006105205111156108b057610540516105205181816020036008021c9050905015613588575b81516001018083528114156107845750506340c10f196105005233610520526104e051610540526020610500604461051c60006103e0515af16108f8573d600060003e3d6000fd5b601f3d11156135885761050050337f26f55a85081d24974e85c6c00045d0f0453991e95873f52bff0d21af4079a76860043561050052602435610520526104a051610540526104c051610560526104605161058052610400516104e051818183011061358857808201905090506105a05260c0610500a26104e0516105005260206105006000600055f35b635e0d443f8118610b5a576004358060801d81607f1d1861358857610420526024358060801d81607f1d1861358857610440526109c16104a06127d9565b6104a080516104605280602001516104805250670de0b6b3a76400006104a0526c0c9f2c9cd04674edea400000006104c0526104606104205160028110156135885760200201516044356104a06104205160028110156135885760200201518082028215828483041417156135885790509050670de0b6b3a764000080820490509050818183011061358857808201905090506104e052610420516102405261044051610260526104e05161028052610460516102a052610480516102c052610a8b610520612c02565b610520516105005261046061044051600281101561358857602002015161050051808210613588578082039050905060018082106135885780820390509050610520526005546105205180820282158284830414171561358857905090506402540be400808204905090506105405261052051610540518082106135885780820390509050670de0b6b3a764000080820282158284830414171561358857905090506104a061044051600281101561358857602002015180801561358857820490509050610560526020610560f35b633df02124811861116d576004358060801d81607f1d1861358857610420526024358060801d81607f1d1861358857610440526000546135885760016000556012546135885760035461046052600454610480526104605160e0526104805161010052610bc86104e0612885565b6104e080516104a05280602001516104c05250670de0b6b3a76400006104e0526c0c9f2c9cd04674edea40000000610500526104a06104205160028110156135885760200201516044356104e06104205160028110156135885760200201518082028215828483041417156135885790509050670de0b6b3a76400008082049050905081818301106135885780820190509050610520526104205161024052610440516102605261052051610280526104a0516102a0526104c0516102c052610c92610560612c02565b61056051610540526104a061044051600281101561358857602002015161054051808210613588578082039050905060018082106135885780820390509050610560526105605160055480820282158284830414171561358857905090506402540be400808204905090506105805261056051610580518082106135885780820390509050670de0b6b3a764000080820282158284830414171561358857905090506104e06104405160028110156135885760200201518080156135885782049050905061056052606435610560511015610e0357602e6105a0527f45786368616e676520726573756c74656420696e20666577657220636f696e736105c0527f207468616e2065787065637465640000000000000000000000000000000000006105e0526105a0506105a051806105c001818260206001820306601f82010390500336823750506308c379a0610560526020610580526105a05160206001820306601f820103905060440161057cfd5b6105805160065480820282158284830414171561358857905090506402540be400808204905090506105a0526105a051670de0b6b3a764000080820282158284830414171561358857905090506104e0610440516002811015613588576020020151808015613588578204905090506105a0526104606104205160028110156135885760200201516044358181830110613588578082019050905060016104205160028110156135885702600301556104606104405160028110156135885760200201516105605180821061358857808203905090506105a0518082106135885780820390509050600161044051600281101561358857026003015560006004610600527f23b872dd00000000000000000000000000000000000000000000000000000000610620526106006004806020846106400101826020850160045afa505080518201915050336020826106400101526020810190503060208261064001015260208101905060443560208261064001015260208101905080610640526106405050602061070061064051610660600060016104205160028110156135885702600101545af1610fbb573d600060003e3d6000fd5b6106e060203d808211610fce5781610fd0565b805b9050905081528051602001806105c0828460045afa9050505060006105c0511115611010576105e0516105c05181816020036008021c9050905015613588575b60006004610600527fa9059cbb00000000000000000000000000000000000000000000000000000000610620526106006004806020846106400101826020850160045afa50508051820191505033602082610640010152602081019050610560516020826106400101526020810190508061064052610640505060206106e061064051610660600060016104405160028110156135885702600101545af16110bd573d600060003e3d6000fd5b6106c060203d8082116110d057816110d2565b805b9050905081528051602001806105c0828460045afa9050505060006105c0511115611112576105e0516105c05181816020036008021c9050905015613588575b337f8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd97140610420516106005260443561062052610440516106405261056051610660526080610600a2610560516106005260206106006000600055f35b635b36389c81186114da5760005461358857600160005560085460e0526318160ddd610120526020610120600461013c60e0515afa6111b1573d600060003e3d6000fd5b601f3d11156135885761012051610100526040366101203761016060006002818352015b60016101605160028110156135885702600301546101805261018051600435808202821582848304141715613588579050905061010051808015613588578204905090506101a05260206101605102602401356101a05110156112ce5760306101c0527f5769746864726177616c20726573756c74656420696e20666577657220636f696101e0527f6e73207468616e20657870656374656400000000000000000000000000000000610200526101c0506101c051806101e001818260206001820306601f82010390500336823750506308c379a06101805260206101a0526101c05160206001820306601f820103905060440161019cfd5b610180516101a051808210613588578082039050905060016101605160028110156135885702600301556101a05161012061016051600281101561358857602002015260006004610200527fa9059cbb00000000000000000000000000000000000000000000000000000000610220526102006004806020846102400101826020850160045afa505080518201915050336020826102400101526020810190506101a0516020826102400101526020810190508061024052610240505060206102e061024051610260600060016101605160028110156135885702600101545af16113be573d600060003e3d6000fd5b6102c060203d8082116113d157816113d3565b805b9050905081528051602001806101c0828460045afa9050505060006101c0511115611413576101e0516101c05181816020036008021c9050905015613588575b81516001018083528114156111d55750506379cc67906101605233610180526004356101a0526020610160604461017c600060e0515af1611459573d600060003e3d6000fd5b601f3d11156135885761016050337f7c363854ccf79623411f8995b362bce5eddff18c927edc6f5dbbb5e05819a82c610120516101605261014051610180526040366101a0376101005160043580821061358857808203905090506101e05260a0610160a26101205161016052610140516101805260406101606000600055f35b63e31032738118611af0576000546135885760016000556012546135885761150361038061269e565b6103805161036052600354610380526004546103a05261038051610240526103a05161026052610360516102805261153c6103e0612ba2565b6103e0516103c052610380516103e0526103a0516104005261042060006002818352015b6103e0610420516002811015613588576020020180516020610420510260040135808210613588578082039050905081525081516001018083528114156115605750506103e05161024052610400516102605261036051610280526115c6610440612ba2565b6104405161042052600554600280820282158284830414171561358857905090506004808204905090506104405260065461046052604036610480376104c060006002818352015b6103e06104c05160028110156135885760200201516104e052610420516103806104c051600281101561358857602002015180820282158284830414171561358857905090506103c05180801561358857820490509050610500526000610520526104e051610500511161169b576104e051610500518082106135885780820390509050610520526116b6565b610500516104e0518082106135885780820390509050610520525b610440516105205180820282158284830414171561358857905090506402540be400808204905090506104806104c05160028110156135885760200201526104e0516104806104c05160028110156135885760200201516104605180820282158284830414171561358857905090506402540be40080820490509050808210613588578082039050905060016104c05160028110156135885702600301556104e0516104806104c051600281101561358857602002015180821061358857808203905090506103e06104c0516002811015613588576020020152815160010180835281141561160e5750506103e05161024052610400516102605261036051610280526117c46104e0612ba2565b6104e0516104c0526008546104e0526318160ddd610520526020610520600461053c6104e0515afa6117fb573d600060003e3d6000fd5b601f3d11156135885761052051610500526103c0516104c05180821061358857808203905090506105005180820282158284830414171561358857905090506103c0518080156135885782049050905061052052600061052051146135885761052080516001818183011061358857808201905090508152506044356105205111156118f8576014610540527f536c697070616765207363726577656420796f750000000000000000000000006105605261054050610540518061056001818260206001820306601f82010390500336823750506308c379a0610500526020610520526105405160206001820306601f820103905060440161051cfd5b6379cc679061054052336105605261052051610580526020610540604461055c60006104e0515af161192f573d600060003e3d6000fd5b601f3d1115613588576105405061054060006002818352015b6000602061054051026004013514611a6357600060046105a0527fa9059cbb000000000000000000000000000000000000000000000000000000006105c0526105a06004806020846105e00101826020850160045afa505080518201915050336020826105e001015260208101905060206105405102600401356020826105e0010152602081019050806105e0526105e0505060206106806105e051610600600060016105405160028110156135885702600101545af1611a0e573d600060003e3d6000fd5b61066060203d808211611a215781611a23565b805b905090508152805160200180610560828460045afa905050506000610560511115611a6357610580516105605181816020036008021c9050905015613588575b8151600101808352811415611948575050337f2b5508378d7e19e0d5fa338419034731416c4f5b219a10379956f764317fd47e600435610540526024356105605261048051610580526104a0516105a052610420516105c052610500516105205180821061358857808203905090506105e05260c0610540a2610520516105405260206105406000600055f35b63cc2b27d78118611b38576024358060801d81607f1d18613588576104805260043561028052610480516102a052611b296104a06131e2565b6104a051610500526020610500f35b631a4d01d28118611e35576024358060801d81607f1d186135885761048052600054613588576001600055601254613588576060366104a03760043561028052610480516102a052611b8b6105006131e2565b61050080516104a05260208101516104c05260408101516104e052506044356104a0511015611c2b576018610500527f4e6f7420656e6f75676820636f696e732072656d6f76656400000000000000006105205261050050610500518061052001818260206001820306601f82010390500336823750506308c379a06104c05260206104e0526105005160206001820306601f82010390506044016104dcfd5b6001610480516002811015613588570260030180546104a0516104c05160065480820282158284830414171561358857905090506402540be400808204905090508181830110613588578082019050905080821061358857808203905090508155506379cc6790610500523361052052600435610540526020610500604461051c60006008545af1611cc2573d600060003e3d6000fd5b601f3d1115613588576105005060006004610540527fa9059cbb00000000000000000000000000000000000000000000000000000000610560526105406004806020846105800101826020850160045afa505080518201915050336020826105800101526020810190506104a051602082610580010152602081019050806105805261058050506020610620610580516105a0600060016104805160028110156135885702600101545af1611d7c573d600060003e3d6000fd5b61060060203d808211611d8f5781611d91565b805b905090508152805160200180610500828460045afa905050506000610500511115611dd157610520516105005181816020036008021c9050905015613588575b337f5ad056f2e28a8cec232015406b843668c1e36cda598127ec3b8c59b8c72773a0600435610540526104a051610560526104e0516004358082106135885780820390509050610580526060610540a26104a0516105405260206105406000600055f35b633c157e648118611f8857600754331861358857600b5462015180818183011061358857808201905090504210613588574262015180818183011061358857808201905090506024351061358857611e8e61018061269e565b61018051610160526004356064808202821582848304141715613588579050905061018052600060043511611ec4576000611ecd565b620f4240600435105b1561358857610160516101805110611f075761016051600a8082028215828483041417156135885790509050610180511161358857611f2b565b6101605161018051600a808202821582848304141715613588579050905010613588575b6101605160095561018051600a5542600b55602435600c557fa2b71ec6df949300b59aab36b55e189697b750119dd349fcfa8c0f779e83c254610160516101a052610180516101c052426101e0526024356102005260806101a0a1005b63551a65888118611ffb57600754331861358857611fa761018061269e565b61018051610160526101605160095561016051600a5542600b5542600c557f46e22fb3709ad289f62ce63d469248536dbc78d82b84a3d7e74ad606dc2019386101605161018052426101a0526040610180a1005b635b5a1467811861209657600754331861358857600d546135885764012a05f20060043511613588576402540be4006024351161358857426203f4808181830110613588578082019050905060e05260e051600d55600435600f5560243560105560e0517f351fc5da2fbf480f2225debf3664a4bc90fa9923743aad58b4603f648e931fe060043561010052602435610120526040610100a2005b634f12fe97811861211457600754331861358857600d544210613588576000600d5414613588576000600d55600f5460e0526010546101005260e051600555610100516006557fbe12859b636aed607d5230b2cc2711f68d70e51060e6cca1f575ef5d2fcc95d160e0516101205261010051610140526040610120a1005b63226840fb811861212f576007543318613588576000600d55005b636b441a4081186121ae576004358060a01c6135885760e052600754331861358857600e5461358857426203f480818183011061358857808201905090506101005261010051600e5560e05160115560e051610100517f181aa3aa17d4cbf99265dd4443eba009433d3cde79d60164fde1d1a192beb9356000610120a3005b636a1c05ae811861221257600754331861358857600e544210613588576000600e5414613588576000600e5560115460e05260e05160075560e0517f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c6000610100a2005b6386fbf193811861222d576007543318613588576000600e55005b63e2e7d26481186122a9576370a0823160e0523061010052602060e0602460fc600160043560028110156135885702600101545afa612271573d600060003e3d6000fd5b601f3d11156135885760e051600160043560028110156135885702600301548082106135885780820390509050610120526020610120f35b6330c54085811861244f5760075433186135885760e060006002818352015b600160e0516002811015613588570260010154610100526370a082316101405230610160526020610140602461015c610100515afa61230c573d600060003e3d6000fd5b601f3d11156135885761014051600160e0516002811015613588570260030154808210613588578082039050905061012052600061012051111561243c5760006004610180527fa9059cbb000000000000000000000000000000000000000000000000000000006101a0526101806004806020846101c00101826020850160045afa505080518201915050336020826101c0010152602081019050610120516020826101c0010152602081019050806101c0526101c0505060206102606101c0516101e06000610100515af16123e7573d600060003e3d6000fd5b61024060203d8082116123fa57816123fc565b805b905090508152805160200180610140828460045afa90505050600061014051111561243c57610160516101405181816020036008021c9050905015613588575b81516001018083528114156122c8575050005b63524c390181186124dd5760075433186135885760e060006002818352015b6370a082316101005230610120526020610100602461011c600160e05160028110156135885702600101545afa6124aa573d600060003e3d6000fd5b601f3d11156135885761010051600160e0516002811015613588570260030155815160010180835281141561246e575050005b63e3698853811861250257600754331861358857426013541115613588576001601255005b633046f972811861251d576007543318613588576000601255005b63c66106578118612544576001600435600281101561358857026001015460e052602060e0f35b634903b0d1811861256b576001600435600281101561358857026003015460e052602060e0f35b63ddca3f4381186125825760055460e052602060e0f35b63fee3f7f981186125995760065460e052602060e0f35b638da5cb5b81186125b05760075460e052602060e0f35b6382c6306681186125c75760085460e052602060e0f35b635409491a81186125de5760095460e052602060e0f35b63b4b577ad81186125f557600a5460e052602060e0f35b632081066c811861260c57600b5460e052602060e0f35b6314052288811861262357600c5460e052602060e0f35b63405e28f8811861263a57600d5460e052602060e0f35b63e0a0b586811861265157600e5460e052602060e0f35b6358680d0b811861266857600f5460e052602060e0f35b63e3824462811861267f5760105460e052602060e0f35b631ec0cdc181186126965760115460e052602060e0f35b505b60006000fd5b600c5460e052600a546101005260e05142106126c457610100518152506127d7566127d7565b60095461012052600b546101405261012051610100511161275c57610120516101205161010051808210613588578082039050905042610140518082106135885780820390509050808202821582848304141715613588579050905060e0516101405180821061358857808203905090508080156135885782049050905080821061358857808203905090508152506127d7566127d7565b610120516101005161012051808210613588578082039050905042610140518082106135885780820390509050808202821582848304141715613588579050905060e05161014051808210613588578082039050905080801561358857820490509050818183011061358857808201905090508152506127d7565b565b670de0b6b3a764000060e0526c0c9f2c9cd04674edea400000006101005261012060006002818352015b60e061012051600281101561358857602002015160016101205160028110156135885702600301548082028215828483041417156135885790509050670de0b6b3a76400008082049050905060e0610120516002811015613588576020020152815160010180835281141561280357505060e051815261010051816020015250565b670de0b6b3a7640000610120526c0c9f2c9cd04674edea400000006101405261016060006002818352015b61012061016051600281101561358857602002015160e06101605160028110156135885760200201518082028215828483041417156135885790509050670de0b6b3a76400008082049050905061012061016051600281101561358857602002015281516001018083528114156128b057505061012051815261014051816020015250565b604036610140376101a060006002818352015b60206101a0510260e001516101805261014080516101805181818301106135885780820190509050815250815160010180835281141561294857505061014051612996576000815250612ba0565b610140516101805261012051600280820282158284830414171561358857905090506101a0526101c0600060ff818352015b610180516101e05261022060006002818352015b6020610220510260e00151610200526101e0516101805180820282158284830414171561358857905090506102005160028082028215828483041417156135885790509050808015613588578204905090506101e05281516001018083528114156129dc57505061018051610160526101a0516101405180820282158284830414171561358857905090506064808204905090506101e05160028082028215828483041417156135885790509050818183011061358857808201905090506101805180820282158284830414171561358857905090506101a0516064808210613588578082039050905061018051808202821582848304141715613588579050905060648082049050905060036101e0518082028215828483041417156135885790509050818183011061358857808201905090508080156135885782049050905061018052610160516101805111612b5e5760016101605161018051808210613588578082039050905011612b8957505061018051815250612ba056612b89565b60016101805161016051808210613588578082039050905011612b8957505061018051815250612ba0565b81516001018083528114156129c857505060006000fd5b565b6102405160e0526102605161010052612bbc6102a0612885565b6102a08051610300528060200151610320525061028051610340526103005160e05261032051610100526103405161012052612bf96102e0612935565b6102e051815250565b6102605161024051146135885760006102605112613588576002610260511215613588576000610240511261358857600261024051121561358857612c4861030061269e565b610300516102e0526102a05160e0526102c051610100526102e05161012052612c72610320612935565b61032051610300526102e05160028082028215828483041417156135885790509050610320526103005161034052606036610360376103c060006002818352015b610240516103c05118612ccd576102805161038052612cfe565b610260516103c0511415612ce457612d6256612cfe565b6102a06103c0516002811015613588576020020151610380525b6103608051610380518181830110613588578082019050905081525061034051610300518082028215828483041417156135885790509050610380516002808202821582848304141715613588579050905080801561358857820490509050610340525b8151600101808352811415612cb35750506103405161030051808202821582848304141715613588579050905060648082028215828483041417156135885790509050610320516002808202821582848304141715613588579050905080801561358857820490509050610340526103605161030051606480820282158284830414171561358857905090506103205180801561358857820490509050818183011061358857808201905090506103c052610300516103e052610400600060ff818352015b6103e0516103a0526103e0516103e0518082028215828483041417156135885790509050610340518181830110613588578082019050905060026103e05180820282158284830414171561358857905090506103c05181818301106135885780820190509050610300518082106135885780820390509050808015613588578204905090506103e0526103a0516103e05111612eec5760016103a0516103e051808210613588578082039050905011612f175750506103e051815250612f2e56612f17565b60016103e0516103a051808210613588578082039050905011612f175750506103e051815250612f2e565b8151600101808352811415612e2757505060006000fd5b565b600061010051126135885760026101005112156135885760e0516002808202821582848304141715613588579050905061018052610160516101a0526060366101c03761022060006002818352015b61010051610220511415612f965761301456612fb0565b6101206102205160028110156135885760200201516101e0525b6101c080516101e051818183011061358857808201905090508152506101a0516101605180820282158284830414171561358857905090506101e05160028082028215828483041417156135885790509050808015613588578204905090506101a0525b8151600101808352811415612f7f5750506101a051610160518082028215828483041417156135885790509050606480820282158284830414171561358857905090506101805160028082028215828483041417156135885790509050808015613588578204905090506101a0526101c0516101605160648082028215828483041417156135885790509050610180518080156135885782049050905081818301106135885780820190509050610220526101605161024052610260600060ff818352015b6102405161020052610240516102405180820282158284830414171561358857905090506101a0518181830110613588578082019050905060026102405180820282158284830414171561358857905090506102205181818301106135885780820190509050610160518082106135885780820390509050808015613588578204905090506102405261020051610240511161319e57600161020051610240518082106135885780820390509050116131c9575050610240518152506131e0566131c9565b600161024051610200518082106135885780820390509050116131c9575050610240518152506131e0565b81516001018083528114156130d957505060006000fd5b565b6131ed6102e061269e565b6102e0516102c0526132006103206127d9565b61032080516102e052806020015161030052506102e05160e05261030051610100526102c05161012052613235610340612935565b61034051610320526318160ddd610360526020610360600461037c6008545afa613264573d600060003e3d6000fd5b601f3d1115613588576103605161034052610320516102805161032051808202821582848304141715613588579050905061034051808015613588578204905090508082106135885780820390509050610360526102c05160e0526102a051610100526102e05161012052610300516101405261036051610160526132ea6103a0612f30565b6103a051610380526102e0516103a052610300516103c052600554600280820282158284830414171561358857905090506004808204905090506103e05261040060006002818352015b6000610420526102a05161040051186133a0576102e0610400516002811015613588576020020151610360518082028215828483041417156135885790509050610320518080156135885782049050905061038051808210613588578082039050905061042052613406565b6102e06104005160028110156135885760200201516102e061040051600281101561358857602002015161036051808202821582848304141715613588579050905061032051808015613588578204905090508082106135885780820390509050610420525b6103a0610400516002811015613588576020020180516103e0516104205180820282158284830414171561358857905090506402540be40080820490509050808210613588578082039050905081525081516001018083528114156133345750506103a06102a05160028110156135885760200201516102c05160e0526102a051610100526103a051610120526103c0516101405261036051610160526134ae610420612f30565b6104205180821061358857808203905090506104005260016104205264e8d4a510006104405261040051600180821061358857808203905090506104206102a051600281101561358857602002015180801561358857820490509050610400526102e06102a05160028110156135885760200201516103805180821061358857808203905090506104206102a0516002811015613588576020020151808015613588578204905090506104605261040051815261046051610400518082106135885780820390509050602082015261034051604082015250565b600080fd5b6101446136d1036101446000396101446136d1036000f35b600080fd000000000000000000000000ecb456ea5365865ebab8a2661b0c503410e9b347000000000000000000000000853d955acef822db058eb8505911ed77f175b99e000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000ecebb5b9ebc11025493f9339f53d1ffb11e2882500000000000000000000000000000000000000000000000000000000000005dc0000000000000000000000000000000000000000000000000000000017d78400000000000000000000000000000000000000000000000000000000012a05f200

Check out more smart contracts

Build blockchain magic with Alchemy

Alchemy combines the most powerful web3 developer products and tools with resources, community and legendary support.