More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 11 from a total of 11 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Remove_liquidity... | 20668478 | 88 days ago | IN | 0 ETH | 0.00036966 | ||||
Remove_liquidity | 20226410 | 150 days ago | IN | 0 ETH | 0.00415078 | ||||
Approve | 19196941 | 294 days ago | IN | 0 ETH | 0.00144748 | ||||
Add_liquidity | 19196938 | 294 days ago | IN | 0 ETH | 0.01003182 | ||||
Approve | 19184743 | 296 days ago | IN | 0 ETH | 0.00245088 | ||||
Approve | 19184739 | 296 days ago | IN | 0 ETH | 0.00377559 | ||||
Add_liquidity | 19184731 | 296 days ago | IN | 0 ETH | 0.01694232 | ||||
Approve | 19182927 | 296 days ago | IN | 0 ETH | 0.00244201 | ||||
Approve | 19131029 | 303 days ago | IN | 0 ETH | 0.001 | ||||
Add_liquidity | 19131017 | 303 days ago | IN | 0 ETH | 0.00543143 | ||||
Add_liquidity | 18640249 | 372 days ago | IN | 0 ETH | 0.01147629 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
18637650 | 373 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x7F86Bf17...dD56c829B The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
CurveTricryptoOptimizedWETH
Compiler Version
vyper:0.3.9
Contract Source Code (Vyper language format)
# @version 0.3.9 """ @title CurveTricryptoOptimizedWETH @author Curve.Fi @license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved @notice A Curve AMM pool for 3 unpegged assets (e.g. ETH, BTC, USD). @dev All prices in the AMM are with respect to the first token in the pool. """ from vyper.interfaces import ERC20 implements: ERC20 # <--------------------- AMM contract is also the LP token. # --------------------------------- Interfaces ------------------------------- interface Math: def geometric_mean(_x: uint256[N_COINS]) -> uint256: view def wad_exp(_power: int256) -> uint256: view def cbrt(x: uint256) -> uint256: view def reduction_coefficient( x: uint256[N_COINS], fee_gamma: uint256 ) -> uint256: view def newton_D( ANN: uint256, gamma: uint256, x_unsorted: uint256[N_COINS], K0_prev: uint256 ) -> uint256: view def get_y( ANN: uint256, gamma: uint256, x: uint256[N_COINS], D: uint256, i: uint256, ) -> uint256[2]: view def get_p( _xp: uint256[N_COINS], _D: uint256, _A_gamma: uint256[2], ) -> uint256[N_COINS-1]: view interface WETH: def deposit(): payable def withdraw(_amount: uint256): nonpayable interface Factory: def admin() -> address: view def fee_receiver() -> address: view def views_implementation() -> address: view interface Views: def calc_token_amount( amounts: uint256[N_COINS], deposit: bool, swap: address ) -> uint256: view def get_dy( i: uint256, j: uint256, dx: uint256, swap: address ) -> uint256: view def get_dx( i: uint256, j: uint256, dy: uint256, swap: address ) -> uint256: view # ------------------------------- Events ------------------------------------- event Transfer: sender: indexed(address) receiver: indexed(address) value: uint256 event Approval: owner: indexed(address) spender: indexed(address) value: uint256 event TokenExchange: buyer: indexed(address) sold_id: uint256 tokens_sold: uint256 bought_id: uint256 tokens_bought: uint256 fee: uint256 packed_price_scale: uint256 event AddLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] fee: uint256 token_supply: uint256 packed_price_scale: uint256 event RemoveLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] token_supply: uint256 event RemoveLiquidityOne: provider: indexed(address) token_amount: uint256 coin_index: uint256 coin_amount: uint256 approx_fee: uint256 packed_price_scale: uint256 event CommitNewParameters: deadline: indexed(uint256) mid_fee: uint256 out_fee: uint256 fee_gamma: uint256 allowed_extra_profit: uint256 adjustment_step: uint256 ma_time: uint256 event NewParameters: mid_fee: uint256 out_fee: uint256 fee_gamma: uint256 allowed_extra_profit: uint256 adjustment_step: uint256 ma_time: uint256 event RampAgamma: initial_A: uint256 future_A: uint256 initial_gamma: uint256 future_gamma: uint256 initial_time: uint256 future_time: uint256 event StopRampA: current_A: uint256 current_gamma: uint256 time: uint256 event ClaimAdminFee: admin: indexed(address) tokens: uint256 # ----------------------- Storage/State Variables ---------------------------- WETH20: public(immutable(address)) N_COINS: constant(uint256) = 3 PRECISION: constant(uint256) = 10**18 # <------- The precision to convert to. A_MULTIPLIER: constant(uint256) = 10000 packed_precisions: uint256 MATH: public(immutable(Math)) coins: public(immutable(address[N_COINS])) factory: public(address) price_scale_packed: uint256 # <------------------------ Internal price scale. price_oracle_packed: uint256 # <------- Price target given by moving average. last_prices_packed: uint256 last_prices_timestamp: public(uint256) initial_A_gamma: public(uint256) initial_A_gamma_time: public(uint256) future_A_gamma: public(uint256) future_A_gamma_time: public(uint256) # <------ Time when ramping is finished. # This value is 0 (default) when pool is first deployed, and only gets # populated by block.timestamp + future_time in `ramp_A_gamma` when the # ramping process is initiated. After ramping is finished # (i.e. self.future_A_gamma_time < block.timestamp), the variable is left # and not set to 0. balances: public(uint256[N_COINS]) D: public(uint256) xcp_profit: public(uint256) xcp_profit_a: public(uint256) # <--- Full profit at last claim of admin fees. virtual_price: public(uint256) # <------ Cached (fast to read) virtual price. # The cached `virtual_price` is also used internally. # -------------- Params that affect how price_scale get adjusted ------------- packed_rebalancing_params: public(uint256) # <---------- Contains rebalancing # parameters allowed_extra_profit, adjustment_step, and ma_time. future_packed_rebalancing_params: uint256 # ---------------- Fee params that determine dynamic fees -------------------- packed_fee_params: public(uint256) # <---- Packs mid_fee, out_fee, fee_gamma. future_packed_fee_params: uint256 ADMIN_FEE: public(constant(uint256)) = 5 * 10**9 # <----- 50% of earned fees. MIN_FEE: constant(uint256) = 5 * 10**5 # <-------------------------- 0.5 BPS. MAX_FEE: constant(uint256) = 10 * 10**9 NOISE_FEE: constant(uint256) = 10**5 # <---------------------------- 0.1 BPS. # ----------------------- Admin params --------------------------------------- admin_actions_deadline: public(uint256) ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400 MIN_RAMP_TIME: constant(uint256) = 86400 MIN_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER / 100 MAX_A: constant(uint256) = 1000 * A_MULTIPLIER * N_COINS**N_COINS MAX_A_CHANGE: constant(uint256) = 10 MIN_GAMMA: constant(uint256) = 10**10 MAX_GAMMA: constant(uint256) = 5 * 10**16 PRICE_SIZE: constant(uint128) = 256 / (N_COINS - 1) PRICE_MASK: constant(uint256) = 2**PRICE_SIZE - 1 # ----------------------- ERC20 Specific vars -------------------------------- name: public(immutable(String[64])) symbol: public(immutable(String[32])) decimals: public(constant(uint8)) = 18 version: public(constant(String[8])) = "v2.0.0" balanceOf: public(HashMap[address, uint256]) allowance: public(HashMap[address, HashMap[address, uint256]]) totalSupply: public(uint256) nonces: public(HashMap[address, uint256]) EIP712_TYPEHASH: constant(bytes32) = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" ) EIP2612_TYPEHASH: constant(bytes32) = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ) VERSION_HASH: constant(bytes32) = keccak256(version) NAME_HASH: immutable(bytes32) CACHED_CHAIN_ID: immutable(uint256) salt: public(immutable(bytes32)) CACHED_DOMAIN_SEPARATOR: immutable(bytes32) # ----------------------- Contract ------------------------------------------- @external def __init__( _name: String[64], _symbol: String[32], _coins: address[N_COINS], _math: address, _weth: address, _salt: bytes32, packed_precisions: uint256, packed_A_gamma: uint256, packed_fee_params: uint256, packed_rebalancing_params: uint256, packed_prices: uint256, ): WETH20 = _weth MATH = Math(_math) self.factory = msg.sender name = _name symbol = _symbol coins = _coins self.packed_precisions = packed_precisions # <------- Precisions of coins # are calculated as 10**(18 - coin.decimals()). self.initial_A_gamma = packed_A_gamma # <------------------- A and gamma. self.future_A_gamma = packed_A_gamma self.packed_rebalancing_params = packed_rebalancing_params # <-- Contains # rebalancing params: allowed_extra_profit, adjustment_step, # and ma_exp_time. self.packed_fee_params = packed_fee_params # <-------------- Contains Fee # params: mid_fee, out_fee and fee_gamma. self.price_scale_packed = packed_prices self.price_oracle_packed = packed_prices self.last_prices_packed = packed_prices self.last_prices_timestamp = block.timestamp self.xcp_profit_a = 10**18 # Cache DOMAIN_SEPARATOR. If chain.id is not CACHED_CHAIN_ID, then # DOMAIN_SEPARATOR will be re-calculated each time `permit` is called. # Otherwise, it will always use CACHED_DOMAIN_SEPARATOR. # see: `_domain_separator()` for its implementation. NAME_HASH = keccak256(name) salt = _salt CACHED_CHAIN_ID = chain.id CACHED_DOMAIN_SEPARATOR = keccak256( _abi_encode( EIP712_TYPEHASH, NAME_HASH, VERSION_HASH, chain.id, self, salt, ) ) log Transfer(empty(address), self, 0) # <------- Fire empty transfer from # 0x0 to self for indexers to catch. # ------------------- Token transfers in and out of the AMM ------------------ @payable @external def __default__(): if msg.value > 0: assert WETH20 in coins @internal def _transfer_in( _coin: address, dx: uint256, dy: uint256, mvalue: uint256, callbacker: address, callback_sig: bytes32, sender: address, receiver: address, use_eth: bool ): """ @notice Transfers `_coin` from `sender` to `self` and calls `callback_sig` if it is not empty. @dev The callback sig must have the following args: sender: address receiver: address coin: address dx: uint256 dy: uint256 @params _coin address of the coin to transfer in. @params dx amount of `_coin` to transfer into the pool. @params dy amount of `_coin` to transfer out of the pool. @params mvalue msg.value if the transfer is ETH, 0 otherwise. @params callbacker address to call `callback_sig` on. @params callback_sig signature of the callback function. @params sender address to transfer `_coin` from. @params receiver address to transfer `_coin` to. @params use_eth True if the transfer is ETH, False otherwise. """ if use_eth and _coin == WETH20: assert mvalue == dx # dev: incorrect eth amount else: assert mvalue == 0 # dev: nonzero eth amount if callback_sig == empty(bytes32): assert ERC20(_coin).transferFrom( sender, self, dx, default_return_value=True ) else: # --------- This part of the _transfer_in logic is only accessible # by _exchange. # First call callback logic and then check if pool # gets dx amounts of _coins[i], revert otherwise. b: uint256 = ERC20(_coin).balanceOf(self) raw_call( callbacker, concat( slice(callback_sig, 0, 4), _abi_encode(sender, receiver, _coin, dx, dy) ) ) assert ERC20(_coin).balanceOf(self) - b == dx # dev: callback didn't give us coins # ^------ note: dx cannot # be 0, so the contract MUST receive some _coin. if _coin == WETH20: WETH(WETH20).withdraw(dx) # <--------- if WETH was transferred in # previous step and `not use_eth`, withdraw WETH to ETH. @internal def _transfer_out( _coin: address, _amount: uint256, use_eth: bool, receiver: address ): """ @notice Transfer a single token from the pool to receiver. @dev This function is called by `remove_liquidity` and `remove_liquidity_one` and `_exchange` methods. @params _coin Address of the token to transfer out @params _amount Amount of token to transfer out @params use_eth Whether to transfer ETH or not @params receiver Address to send the tokens to """ if use_eth and _coin == WETH20: raw_call(receiver, b"", value=_amount) else: if _coin == WETH20: WETH(WETH20).deposit(value=_amount) assert ERC20(_coin).transfer( receiver, _amount, default_return_value=True ) # -------------------------- AMM Main Functions ------------------------------ @payable @external @nonreentrant("lock") def exchange( i: uint256, j: uint256, dx: uint256, min_dy: uint256, use_eth: bool = False, receiver: address = msg.sender ) -> uint256: """ @notice Exchange using wrapped native token by default @param i Index value for the input coin @param j Index value for the output coin @param dx Amount of input coin being swapped in @param min_dy Minimum amount of output coin to receive @param use_eth True if the input coin is native token, False otherwise @param receiver Address to send the output coin to. Default is msg.sender @return uint256 Amount of tokens at index j received by the `receiver """ return self._exchange( msg.sender, msg.value, i, j, dx, min_dy, use_eth, receiver, empty(address), empty(bytes32) ) @payable @external @nonreentrant('lock') def exchange_underlying( i: uint256, j: uint256, dx: uint256, min_dy: uint256, receiver: address = msg.sender ) -> uint256: """ @notice Exchange using native token transfers. @param i Index value for the input coin @param j Index value for the output coin @param dx Amount of input coin being swapped in @param min_dy Minimum amount of output coin to receive @param receiver Address to send the output coin to. Default is msg.sender @return uint256 Amount of tokens at index j received by the `receiver """ return self._exchange( msg.sender, msg.value, i, j, dx, min_dy, True, receiver, empty(address), empty(bytes32) ) @external @nonreentrant('lock') def exchange_extended( i: uint256, j: uint256, dx: uint256, min_dy: uint256, use_eth: bool, sender: address, receiver: address, cb: bytes32 ) -> uint256: """ @notice Exchange with callback method. @dev This method does not allow swapping in native token, but does allow swaps that transfer out native token from the pool. @dev Does not allow flashloans @dev One use-case is to reduce the number of redundant ERC20 token transfers in zaps. @param i Index value for the input coin @param j Index value for the output coin @param dx Amount of input coin being swapped in @param min_dy Minimum amount of output coin to receive @param use_eth True if output is native token, False otherwise @param sender Address to transfer input coin from @param receiver Address to send the output coin to @param cb Callback signature @return uint256 Amount of tokens at index j received by the `receiver` """ assert cb != empty(bytes32) # dev: No callback specified return self._exchange( sender, 0, i, j, dx, min_dy, use_eth, receiver, msg.sender, cb ) # callbacker should never be self ------------------^ @payable @external @nonreentrant("lock") def add_liquidity( amounts: uint256[N_COINS], min_mint_amount: uint256, use_eth: bool = False, receiver: address = msg.sender ) -> uint256: """ @notice Adds liquidity into the pool. @param amounts Amounts of each coin to add. @param min_mint_amount Minimum amount of LP to mint. @param use_eth True if native token is being added to the pool. @param receiver Address to send the LP tokens to. Default is msg.sender @return uint256 Amount of LP tokens received by the `receiver """ A_gamma: uint256[2] = self._A_gamma() xp: uint256[N_COINS] = self.balances amountsp: uint256[N_COINS] = empty(uint256[N_COINS]) xx: uint256[N_COINS] = empty(uint256[N_COINS]) d_token: uint256 = 0 d_token_fee: uint256 = 0 old_D: uint256 = 0 assert amounts[0] + amounts[1] + amounts[2] > 0 # dev: no coins to add # --------------------- Get prices, balances ----------------------------- precisions: uint256[N_COINS] = self._unpack(self.packed_precisions) packed_price_scale: uint256 = self.price_scale_packed price_scale: uint256[N_COINS-1] = self._unpack_prices(packed_price_scale) # -------------------------------------- Update balances and calculate xp. xp_old: uint256[N_COINS] = xp for i in range(N_COINS): bal: uint256 = xp[i] + amounts[i] xp[i] = bal self.balances[i] = bal xx = xp xp[0] *= precisions[0] xp_old[0] *= precisions[0] for i in range(1, N_COINS): xp[i] = unsafe_div(xp[i] * price_scale[i-1] * precisions[i], PRECISION) xp_old[i] = unsafe_div( xp_old[i] * unsafe_mul(price_scale[i-1], precisions[i]), PRECISION ) # ---------------- transferFrom token into the pool ---------------------- for i in range(N_COINS): if amounts[i] > 0: if coins[i] == WETH20: self._transfer_in( coins[i], amounts[i], 0, # <----------------------------------- msg.value, # | No callbacks empty(address), # <----------------------| for empty(bytes32), # <----------------------| add_liquidity. msg.sender, # | empty(address), # <----------------------- use_eth ) else: self._transfer_in( coins[i], amounts[i], 0, 0, # <----------------- mvalue = 0 if coin is not WETH20. empty(address), empty(bytes32), msg.sender, empty(address), False # <-------- use_eth is False if coin is not WETH20. ) amountsp[i] = xp[i] - xp_old[i] # -------------------- Calculate LP tokens to mint ----------------------- if self.future_A_gamma_time > block.timestamp: # <--- A_gamma is ramping. # ----- Recalculate the invariant if A or gamma are undergoing a ramp. old_D = MATH.newton_D(A_gamma[0], A_gamma[1], xp_old, 0) else: old_D = self.D D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) token_supply: uint256 = self.totalSupply if old_D > 0: d_token = token_supply * D / old_D - token_supply else: d_token = self.get_xcp(D) # <------------------------- Making initial # virtual price equal to 1. assert d_token > 0 # dev: nothing minted if old_D > 0: d_token_fee = ( self._calc_token_fee(amountsp, xp) * d_token / 10**10 + 1 ) d_token -= d_token_fee token_supply += d_token self.mint(receiver, d_token) packed_price_scale = self.tweak_price(A_gamma, xp, D, 0) else: self.D = D self.virtual_price = 10**18 self.xcp_profit = 10**18 self.xcp_profit_a = 10**18 self.mint(receiver, d_token) assert d_token >= min_mint_amount, "Slippage" log AddLiquidity( receiver, amounts, d_token_fee, token_supply, packed_price_scale ) self._claim_admin_fees() # <--------------------------- Claim admin fees. return d_token @external @nonreentrant("lock") def remove_liquidity( _amount: uint256, min_amounts: uint256[N_COINS], use_eth: bool = False, receiver: address = msg.sender, claim_admin_fees: bool = True, ) -> uint256[N_COINS]: """ @notice This withdrawal method is very safe, does no complex math since tokens are withdrawn in balanced proportions. No fees are charged. @param _amount Amount of LP tokens to burn @param min_amounts Minimum amounts of tokens to withdraw @param use_eth Whether to withdraw ETH or not @param receiver Address to send the withdrawn tokens to @param claim_admin_fees If True, call self._claim_admin_fees(). Default is True. @return uint256[3] Amount of pool tokens received by the `receiver` """ amount: uint256 = _amount balances: uint256[N_COINS] = self.balances d_balances: uint256[N_COINS] = empty(uint256[N_COINS]) if claim_admin_fees: self._claim_admin_fees() # <------ We claim fees so that the DAO gets # paid before withdrawal. In emergency cases, set it to False. # -------------------------------------------------------- Burn LP tokens. total_supply: uint256 = self.totalSupply # <------ Get totalSupply before self.burnFrom(msg.sender, _amount) # ---- reducing it with self.burnFrom. # There are two cases for withdrawing tokens from the pool. # Case 1. Withdrawal does not empty the pool. # In this situation, D is adjusted proportional to the amount of # LP tokens burnt. ERC20 tokens transferred is proportional # to : (AMM balance * LP tokens in) / LP token total supply # Case 2. Withdrawal empties the pool. # In this situation, all tokens are withdrawn and the invariant # is reset. if amount == total_supply: # <----------------------------------- Case 2. for i in range(N_COINS): d_balances[i] = balances[i] self.balances[i] = 0 # <------------------------- Empty the pool. else: # <-------------------------------------------------------- Case 1. amount -= 1 # <---- To prevent rounding errors, favor LPs a tiny bit. for i in range(N_COINS): d_balances[i] = balances[i] * amount / total_supply assert d_balances[i] >= min_amounts[i] self.balances[i] = balances[i] - d_balances[i] balances[i] = d_balances[i] # <-- Now it's the amounts going out. D: uint256 = self.D self.D = D - unsafe_div(D * amount, total_supply) # <----------- Reduce D # proportional to the amount of tokens leaving. Since withdrawals are # balanced, this is a simple subtraction. If amount == total_supply, # D will be 0. # ---------------------------------- Transfers --------------------------- for i in range(N_COINS): self._transfer_out(coins[i], d_balances[i], use_eth, receiver) log RemoveLiquidity(msg.sender, balances, total_supply - _amount) return d_balances @external @nonreentrant("lock") def remove_liquidity_one_coin( token_amount: uint256, i: uint256, min_amount: uint256, use_eth: bool = False, receiver: address = msg.sender ) -> uint256: """ @notice Withdraw liquidity in a single token. Involves fees (lower than swap fees). @dev This operation also involves an admin fee claim. @param token_amount Amount of LP tokens to burn @param i Index of the token to withdraw @param min_amount Minimum amount of token to withdraw. @param use_eth Whether to withdraw ETH or not @param receiver Address to send the withdrawn tokens to @return Amount of tokens at index i received by the `receiver` """ A_gamma: uint256[2] = self._A_gamma() dy: uint256 = 0 D: uint256 = 0 p: uint256 = 0 xp: uint256[N_COINS] = empty(uint256[N_COINS]) approx_fee: uint256 = 0 # ---------------------------- Claim admin fees before removing liquidity. self._claim_admin_fees() # ------------------------------------------------------------------------ dy, D, xp, approx_fee = self._calc_withdraw_one_coin( A_gamma, token_amount, i, (self.future_A_gamma_time > block.timestamp), # <------- During ramps ) # we need to update D. assert dy >= min_amount, "Slippage" # ------------------------- Transfers ------------------------------------ self.balances[i] -= dy self.burnFrom(msg.sender, token_amount) self._transfer_out(coins[i], dy, use_eth, receiver) packed_price_scale: uint256 = self.tweak_price(A_gamma, xp, D, 0) # Safe to use D from _calc_withdraw_one_coin here ---^ log RemoveLiquidityOne( msg.sender, token_amount, i, dy, approx_fee, packed_price_scale ) return dy @external @nonreentrant("lock") def claim_admin_fees(): """ @notice Claim admin fees. Callable by anyone. """ self._claim_admin_fees() # -------------------------- Packing functions ------------------------------- @internal @view def _pack(x: uint256[3]) -> uint256: """ @notice Packs 3 integers with values <= 10**18 into a uint256 @param x The uint256[3] to pack @return uint256 Integer with packed values """ return (x[0] << 128) | (x[1] << 64) | x[2] @internal @view def _unpack(_packed: uint256) -> uint256[3]: """ @notice Unpacks a uint256 into 3 integers (values must be <= 10**18) @param val The uint256 to unpack @return uint256[3] A list of length 3 with unpacked integers """ return [ (_packed >> 128) & 18446744073709551615, (_packed >> 64) & 18446744073709551615, _packed & 18446744073709551615, ] @internal @view def _pack_prices(prices_to_pack: uint256[N_COINS-1]) -> uint256: """ @notice Packs N_COINS-1 prices into a uint256. @param prices_to_pack The prices to pack @return uint256 An integer that packs prices """ packed_prices: uint256 = 0 p: uint256 = 0 for k in range(N_COINS - 1): packed_prices = packed_prices << PRICE_SIZE p = prices_to_pack[N_COINS - 2 - k] assert p < PRICE_MASK packed_prices = p | packed_prices return packed_prices @internal @view def _unpack_prices(_packed_prices: uint256) -> uint256[2]: """ @notice Unpacks N_COINS-1 prices from a uint256. @param _packed_prices The packed prices @return uint256[2] Unpacked prices """ unpacked_prices: uint256[N_COINS-1] = empty(uint256[N_COINS-1]) packed_prices: uint256 = _packed_prices for k in range(N_COINS - 1): unpacked_prices[k] = packed_prices & PRICE_MASK packed_prices = packed_prices >> PRICE_SIZE return unpacked_prices # ---------------------- AMM Internal Functions ------------------------------- @internal def _exchange( sender: address, mvalue: uint256, i: uint256, j: uint256, dx: uint256, min_dy: uint256, use_eth: bool, receiver: address, callbacker: address, callback_sig: bytes32 ) -> uint256: assert i != j # dev: coin index out of range assert dx > 0 # dev: do not exchange 0 coins A_gamma: uint256[2] = self._A_gamma() xp: uint256[N_COINS] = self.balances precisions: uint256[N_COINS] = self._unpack(self.packed_precisions) dy: uint256 = 0 y: uint256 = xp[j] # <----------------- if j > N_COINS, this will revert. x0: uint256 = xp[i] # <--------------- if i > N_COINS, this will revert. xp[i] = x0 + dx self.balances[i] = xp[i] packed_price_scale: uint256 = self.price_scale_packed price_scale: uint256[N_COINS - 1] = self._unpack_prices( packed_price_scale ) xp[0] *= precisions[0] for k in range(1, N_COINS): xp[k] = unsafe_div( xp[k] * price_scale[k - 1] * precisions[k], PRECISION ) # <-------- Safu to do unsafe_div here since PRECISION is not zero. prec_i: uint256 = precisions[i] # ----------- Update invariant if A, gamma are undergoing ramps --------- t: uint256 = self.future_A_gamma_time if t > block.timestamp: x0 *= prec_i if i > 0: x0 = unsafe_div(x0 * price_scale[i - 1], PRECISION) x1: uint256 = xp[i] # <------------------ Back up old value in xp ... xp[i] = x0 # | self.D = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) # | xp[i] = x1 # <-------------------------------------- ... and restore. # ----------------------- Calculate dy and fees -------------------------- D: uint256 = self.D prec_j: uint256 = precisions[j] y_out: uint256[2] = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, j) dy = xp[j] - y_out[0] xp[j] -= dy dy -= 1 if j > 0: dy = dy * PRECISION / price_scale[j - 1] dy /= prec_j fee: uint256 = unsafe_div(self._fee(xp) * dy, 10**10) dy -= fee # <--------------------- Subtract fee from the outgoing amount. assert dy >= min_dy, "Slippage" y -= dy self.balances[j] = y # <----------- Update pool balance of outgoing coin. y *= prec_j if j > 0: y = unsafe_div(y * price_scale[j - 1], PRECISION) xp[j] = y # <------------------------------------------------- Update xp. # ---------------------- Do Transfers in and out ------------------------- ########################## TRANSFER IN <------- self._transfer_in( coins[i], dx, dy, mvalue, callbacker, callback_sig, # <-------- Callback method is called here. sender, receiver, use_eth, ) ########################## -------> TRANSFER OUT self._transfer_out(coins[j], dy, use_eth, receiver) # ------ Tweak price_scale with good initial guess for newton_D ---------- packed_price_scale = self.tweak_price(A_gamma, xp, 0, y_out[1]) log TokenExchange(sender, i, dx, j, dy, fee, packed_price_scale) return dy @internal def tweak_price( A_gamma: uint256[2], _xp: uint256[N_COINS], new_D: uint256, K0_prev: uint256 = 0, ) -> uint256: """ @notice Tweaks price_oracle, last_price and conditionally adjusts price_scale. This is called whenever there is an unbalanced liquidity operation: _exchange, add_liquidity, or remove_liquidity_one_coin. @dev Contains main liquidity rebalancing logic, by tweaking `price_scale`. @param A_gamma Array of A and gamma parameters. @param _xp Array of current balances. @param new_D New D value. @param K0_prev Initial guess for `newton_D`. """ # ---------------------------- Read storage ------------------------------ rebalancing_params: uint256[3] = self._unpack( self.packed_rebalancing_params ) # <---------- Contains: allowed_extra_profit, adjustment_step, ma_time. price_oracle: uint256[N_COINS - 1] = self._unpack_prices( self.price_oracle_packed ) last_prices: uint256[N_COINS - 1] = self._unpack_prices( self.last_prices_packed ) packed_price_scale: uint256 = self.price_scale_packed price_scale: uint256[N_COINS - 1] = self._unpack_prices( packed_price_scale ) total_supply: uint256 = self.totalSupply old_xcp_profit: uint256 = self.xcp_profit old_virtual_price: uint256 = self.virtual_price last_prices_timestamp: uint256 = self.last_prices_timestamp # ----------------------- Update MA if needed ---------------------------- if last_prices_timestamp < block.timestamp: # The moving average price oracle is calculated using the last_price # of the trade at the previous block, and the price oracle logged # before that trade. This can happen only once per block. # ------------------ Calculate moving average params ----------------- alpha: uint256 = MATH.wad_exp( -convert( unsafe_div( (block.timestamp - last_prices_timestamp) * 10**18, rebalancing_params[2] # <----------------------- ma_time. ), int256, ) ) for k in range(N_COINS - 1): # ----------------- We cap state price that goes into the EMA with # 2 x price_scale. price_oracle[k] = unsafe_div( min(last_prices[k], 2 * price_scale[k]) * (10**18 - alpha) + price_oracle[k] * alpha, # ^-------- Cap spot price into EMA. 10**18 ) self.price_oracle_packed = self._pack_prices(price_oracle) self.last_prices_timestamp = block.timestamp # <---- Store timestamp. # price_oracle is used further on to calculate its vector # distance from price_scale. This distance is used to calculate # the amount of adjustment to be done to the price_scale. # ------------------ If new_D is set to 0, calculate it ------------------ D_unadjusted: uint256 = new_D if new_D == 0: # <--------------------------- _exchange sets new_D to 0. D_unadjusted = MATH.newton_D(A_gamma[0], A_gamma[1], _xp, K0_prev) # ----------------------- Calculate last_prices -------------------------- last_prices = MATH.get_p(_xp, D_unadjusted, A_gamma) for k in range(N_COINS - 1): last_prices[k] = unsafe_div(last_prices[k] * price_scale[k], 10**18) self.last_prices_packed = self._pack_prices(last_prices) # ---------- Update profit numbers without price adjustment first -------- xp: uint256[N_COINS] = empty(uint256[N_COINS]) xp[0] = unsafe_div(D_unadjusted, N_COINS) for k in range(N_COINS - 1): xp[k + 1] = D_unadjusted * 10**18 / (N_COINS * price_scale[k]) # ------------------------- Update xcp_profit ---------------------------- xcp_profit: uint256 = 10**18 virtual_price: uint256 = 10**18 if old_virtual_price > 0: xcp: uint256 = MATH.geometric_mean(xp) virtual_price = 10**18 * xcp / total_supply xcp_profit = unsafe_div( old_xcp_profit * virtual_price, old_virtual_price ) # <---------------- Safu to do unsafe_div as old_virtual_price > 0. # If A and gamma are not undergoing ramps (t < block.timestamp), # ensure new virtual_price is not less than old virtual_price, # else the pool suffers a loss. if self.future_A_gamma_time < block.timestamp: assert virtual_price > old_virtual_price, "Loss" self.xcp_profit = xcp_profit # ------------ Rebalance liquidity if there's enough profits to adjust it: if virtual_price * 2 - 10**18 > xcp_profit + 2 * rebalancing_params[0]: # allowed_extra_profit --------^ # ------------------- Get adjustment step ---------------------------- # Calculate the vector distance between price_scale and # price_oracle. norm: uint256 = 0 ratio: uint256 = 0 for k in range(N_COINS - 1): ratio = unsafe_div(price_oracle[k] * 10**18, price_scale[k]) # unsafe_div because we did safediv before ----^ if ratio > 10**18: ratio = unsafe_sub(ratio, 10**18) else: ratio = unsafe_sub(10**18, ratio) norm = unsafe_add(norm, ratio**2) norm = isqrt(norm) # <-------------------- isqrt is not in base 1e18. adjustment_step: uint256 = max( rebalancing_params[1], unsafe_div(norm, 5) ) # ^------------------------------------- adjustment_step. if norm > adjustment_step: # <---------- We only adjust prices if the # vector distance between price_oracle and price_scale is # large enough. This check ensures that no rebalancing # occurs if the distance is low i.e. the pool prices are # pegged to the oracle prices. # ------------------------------------- Calculate new price scale. p_new: uint256[N_COINS - 1] = empty(uint256[N_COINS - 1]) for k in range(N_COINS - 1): p_new[k] = unsafe_div( price_scale[k] * unsafe_sub(norm, adjustment_step) + adjustment_step * price_oracle[k], norm ) # <- norm is non-zero and gt adjustment_step; unsafe = safe # ---------------- Update stale xp (using price_scale) with p_new. xp = _xp for k in range(N_COINS - 1): xp[k + 1] = unsafe_div(_xp[k + 1] * p_new[k], price_scale[k]) # unsafe_div because we did safediv before ----^ # ------------------------------------------ Update D with new xp. D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) for k in range(N_COINS): frac: uint256 = xp[k] * 10**18 / D # <----- Check validity of assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # p_new. xp[0] = D / N_COINS for k in range(N_COINS - 1): xp[k + 1] = D * 10**18 / (N_COINS * p_new[k]) # <---- Convert # xp to real prices. # ---------- Calculate new virtual_price using new xp and D. Reuse # `old_virtual_price` (but it has new virtual_price). old_virtual_price = unsafe_div( 10**18 * MATH.geometric_mean(xp), total_supply ) # <----- unsafe_div because we did safediv before (if vp>1e18) # ---------------------------- Proceed if we've got enough profit. if ( old_virtual_price > 10**18 and 2 * old_virtual_price - 10**18 > xcp_profit ): packed_price_scale = self._pack_prices(p_new) self.D = D self.virtual_price = old_virtual_price self.price_scale_packed = packed_price_scale return packed_price_scale # --------- price_scale was not adjusted. Update the profit counter and D. self.D = D_unadjusted self.virtual_price = virtual_price return packed_price_scale @internal def _claim_admin_fees(): """ @notice Claims admin fees and sends it to fee_receiver set in the factory. """ A_gamma: uint256[2] = self._A_gamma() xcp_profit: uint256 = self.xcp_profit # <---------- Current pool profits. xcp_profit_a: uint256 = self.xcp_profit_a # <- Profits at previous claim. total_supply: uint256 = self.totalSupply # Do not claim admin fees if: # 1. insufficient profits accrued since last claim, and # 2. there are less than 10**18 (or 1 unit of) lp tokens, else it can lead # to manipulated virtual prices. if xcp_profit <= xcp_profit_a or total_supply < 10**18: return # Claim tokens belonging to the admin here. This is done by 'gulping' # pool tokens that have accrued as fees, but not accounted in pool's # `self.balances` yet: pool balances only account for incoming and # outgoing tokens excluding fees. Following 'gulps' fees: for i in range(N_COINS): if coins[i] == WETH20: self.balances[i] = self.balance else: self.balances[i] = ERC20(coins[i]).balanceOf(self) # If the pool has made no profits, `xcp_profit == xcp_profit_a` # and the pool gulps nothing in the previous step. vprice: uint256 = self.virtual_price # Admin fees are calculated as follows. # 1. Calculate accrued profit since last claim. `xcp_profit` # is the current profits. `xcp_profit_a` is the profits # at the previous claim. # 2. Take out admin's share, which is hardcoded at 5 * 10**9. # (50% => half of 100% => 10**10 / 2 => 5 * 10**9). # 3. Since half of the profits go to rebalancing the pool, we # are left with half; so divide by 2. fees: uint256 = unsafe_div( unsafe_sub(xcp_profit, xcp_profit_a) * ADMIN_FEE, 2 * 10**10 ) # ------------------------------ Claim admin fees by minting admin's share # of the pool in LP tokens. receiver: address = Factory(self.factory).fee_receiver() if receiver != empty(address) and fees > 0: frac: uint256 = vprice * 10**18 / (vprice - fees) - 10**18 claimed: uint256 = self.mint_relative(receiver, frac) xcp_profit -= fees * 2 self.xcp_profit = xcp_profit log ClaimAdminFee(receiver, claimed) # ------------------------------------------- Recalculate D b/c we gulped. D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], self.xp(), 0) self.D = D # ------------------- Recalculate virtual_price following admin fee claim. # In this instance we do not check if current virtual price is greater # than old virtual price, since the claim process can result # in a small decrease in pool's value. self.virtual_price = 10**18 * self.get_xcp(D) / self.totalSupply self.xcp_profit_a = xcp_profit # <------------ Cache last claimed profit. @internal @view def xp() -> uint256[N_COINS]: result: uint256[N_COINS] = self.balances packed_prices: uint256 = self.price_scale_packed precisions: uint256[N_COINS] = self._unpack(self.packed_precisions) result[0] *= precisions[0] for i in range(1, N_COINS): p: uint256 = (packed_prices & PRICE_MASK) * precisions[i] result[i] = result[i] * p / PRECISION packed_prices = packed_prices >> PRICE_SIZE return result @view @internal def _A_gamma() -> uint256[2]: t1: uint256 = self.future_A_gamma_time A_gamma_1: uint256 = self.future_A_gamma gamma1: uint256 = A_gamma_1 & 2**128 - 1 A1: uint256 = A_gamma_1 >> 128 if block.timestamp < t1: # --------------- Handle ramping up and down of A -------------------- A_gamma_0: uint256 = self.initial_A_gamma t0: uint256 = self.initial_A_gamma_time t1 -= t0 t0 = block.timestamp - t0 t2: uint256 = t1 - t0 A1 = ((A_gamma_0 >> 128) * t2 + A1 * t0) / t1 gamma1 = ((A_gamma_0 & 2**128 - 1) * t2 + gamma1 * t0) / t1 return [A1, gamma1] @internal @view def _fee(xp: uint256[N_COINS]) -> uint256: fee_params: uint256[3] = self._unpack(self.packed_fee_params) f: uint256 = MATH.reduction_coefficient(xp, fee_params[2]) return unsafe_div( fee_params[0] * f + fee_params[1] * (10**18 - f), 10**18 ) @internal @view def get_xcp(D: uint256) -> uint256: x: uint256[N_COINS] = empty(uint256[N_COINS]) x[0] = D / N_COINS packed_prices: uint256 = self.price_scale_packed # <-- No precisions here # because we don't switch to "real" units. for i in range(1, N_COINS): x[i] = D * 10**18 / (N_COINS * (packed_prices & PRICE_MASK)) packed_prices = packed_prices >> PRICE_SIZE return MATH.geometric_mean(x) @view @internal def _calc_token_fee(amounts: uint256[N_COINS], xp: uint256[N_COINS]) -> uint256: # fee = sum(amounts_i - avg(amounts)) * fee' / sum(amounts) fee: uint256 = unsafe_div( unsafe_mul(self._fee(xp), N_COINS), unsafe_mul(4, unsafe_sub(N_COINS, 1)) ) S: uint256 = 0 for _x in amounts: S += _x avg: uint256 = unsafe_div(S, N_COINS) Sdiff: uint256 = 0 for _x in amounts: if _x > avg: Sdiff += unsafe_sub(_x, avg) else: Sdiff += unsafe_sub(avg, _x) return fee * Sdiff / S + NOISE_FEE @internal @view def _calc_withdraw_one_coin( A_gamma: uint256[2], token_amount: uint256, i: uint256, update_D: bool, ) -> (uint256, uint256, uint256[N_COINS], uint256): token_supply: uint256 = self.totalSupply assert token_amount <= token_supply # dev: token amount more than supply assert i < N_COINS # dev: coin out of range xx: uint256[N_COINS] = self.balances precisions: uint256[N_COINS] = self._unpack(self.packed_precisions) xp: uint256[N_COINS] = precisions D0: uint256 = 0 # -------------------------- Calculate D0 and xp ------------------------- price_scale_i: uint256 = PRECISION * precisions[0] packed_prices: uint256 = self.price_scale_packed xp[0] *= xx[0] for k in range(1, N_COINS): p: uint256 = (packed_prices & PRICE_MASK) if i == k: price_scale_i = p * xp[i] xp[k] = unsafe_div(xp[k] * xx[k] * p, PRECISION) packed_prices = packed_prices >> PRICE_SIZE if update_D: # <-------------- D is updated if pool is undergoing a ramp. D0 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) else: D0 = self.D D: uint256 = D0 # -------------------------------- Fee Calc ------------------------------ # Charge fees on D. Roughly calculate xp[i] after withdrawal and use that # to calculate fee. Precision is not paramount here: we just want a # behavior where the higher the imbalance caused the more fee the AMM # charges. # xp is adjusted assuming xp[0] ~= xp[1] ~= x[2], which is usually not the # case. We charge self._fee(xp), where xp is an imprecise adjustment post # withdrawal in one coin. If the withdraw is too large: charge max fee by # default. This is because the fee calculation will otherwise underflow. xp_imprecise: uint256[N_COINS] = xp xp_correction: uint256 = xp[i] * N_COINS * token_amount / token_supply fee: uint256 = self._unpack(self.packed_fee_params)[1] # <- self.out_fee. if xp_correction < xp_imprecise[i]: xp_imprecise[i] -= xp_correction fee = self._fee(xp_imprecise) dD: uint256 = unsafe_div(token_amount * D, token_supply) D_fee: uint256 = fee * dD / (2 * 10**10) + 1 # <------- Actual fee on D. # --------- Calculate `approx_fee` (assuming balanced state) in ith token. # -------------------------------- We only need this for fee in the event. approx_fee: uint256 = N_COINS * D_fee * xx[i] / D # ------------------------------------------------------------------------ D -= (dD - D_fee) # <----------------------------------- Charge fee on D. # --------------------------------- Calculate `y_out`` with `(D - D_fee)`. y: uint256 = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, i)[0] dy: uint256 = (xp[i] - y) * PRECISION / price_scale_i xp[i] = y return dy, D, xp, approx_fee # ------------------------ ERC20 functions ----------------------------------- @internal def _approve(_owner: address, _spender: address, _value: uint256): self.allowance[_owner][_spender] = _value log Approval(_owner, _spender, _value) @internal def _transfer(_from: address, _to: address, _value: uint256): assert _to not in [self, empty(address)] self.balanceOf[_from] -= _value self.balanceOf[_to] += _value log Transfer(_from, _to, _value) @view @internal def _domain_separator() -> bytes32: if chain.id != CACHED_CHAIN_ID: return keccak256( _abi_encode( EIP712_TYPEHASH, NAME_HASH, VERSION_HASH, chain.id, self, salt, ) ) return CACHED_DOMAIN_SEPARATOR @external def transferFrom(_from: address, _to: address, _value: uint256) -> bool: """ @dev Transfer tokens from one address to another. @param _from address The address which you want to send tokens from @param _to address The address which you want to transfer to @param _value uint256 the amount of tokens to be transferred @return bool True on successul transfer. Reverts otherwise. """ _allowance: uint256 = self.allowance[_from][msg.sender] if _allowance != max_value(uint256): self._approve(_from, msg.sender, _allowance - _value) self._transfer(_from, _to, _value) return True @external def transfer(_to: address, _value: uint256) -> bool: """ @dev Transfer token for a specified address @param _to The address to transfer to. @param _value The amount to be transferred. @return bool True on successful transfer. Reverts otherwise. """ self._transfer(msg.sender, _to, _value) return True @external def approve(_spender: address, _value: uint256) -> bool: """ @notice Allow `_spender` to transfer up to `_value` amount of tokens from the caller's account. @dev Non-zero to non-zero approvals are allowed, but should be used cautiously. The methods increaseAllowance + decreaseAllowance are available to prevent any front-running that may occur. @param _spender The account permitted to spend up to `_value` amount of caller's funds. @param _value The amount of tokens `_spender` is allowed to spend. @return bool Success """ self._approve(msg.sender, _spender, _value) return True @external def increaseAllowance(_spender: address, _add_value: uint256) -> bool: """ @notice Increase the allowance granted to `_spender`. @dev This function will never overflow, and instead will bound allowance to max_value(uint256). This has the potential to grant an infinite approval. @param _spender The account to increase the allowance of. @param _add_value The amount to increase the allowance by. @return bool Success """ cached_allowance: uint256 = self.allowance[msg.sender][_spender] allowance: uint256 = unsafe_add(cached_allowance, _add_value) if allowance < cached_allowance: # <-------------- Check for an overflow. allowance = max_value(uint256) if allowance != cached_allowance: self._approve(msg.sender, _spender, allowance) return True @external def decreaseAllowance(_spender: address, _sub_value: uint256) -> bool: """ @notice Decrease the allowance granted to `_spender`. @dev This function will never underflow, and instead will bound allowance to 0. @param _spender The account to decrease the allowance of. @param _sub_value The amount to decrease the allowance by. @return bool Success. """ cached_allowance: uint256 = self.allowance[msg.sender][_spender] allowance: uint256 = unsafe_sub(cached_allowance, _sub_value) if cached_allowance < allowance: # <------------- Check for an underflow. allowance = 0 if allowance != cached_allowance: self._approve(msg.sender, _spender, allowance) return True @external def permit( _owner: address, _spender: address, _value: uint256, _deadline: uint256, _v: uint8, _r: bytes32, _s: bytes32, ) -> bool: """ @notice Permit `_spender` to spend up to `_value` amount of `_owner`'s tokens via a signature. @dev In the event of a chain fork, replay attacks are prevented as domain separator is recalculated. However, this is only if the resulting chains update their chainId. @param _owner The account which generated the signature and is granting an allowance. @param _spender The account which will be granted an allowance. @param _value The approval amount. @param _deadline The deadline by which the signature must be submitted. @param _v The last byte of the ECDSA signature. @param _r The first 32 bytes of the ECDSA signature. @param _s The second 32 bytes of the ECDSA signature. @return bool Success. """ assert _owner != empty(address) # dev: invalid owner assert block.timestamp <= _deadline # dev: permit expired nonce: uint256 = self.nonces[_owner] digest: bytes32 = keccak256( concat( b"\x19\x01", self._domain_separator(), keccak256( _abi_encode( EIP2612_TYPEHASH, _owner, _spender, _value, nonce, _deadline ) ), ) ) assert ecrecover(digest, _v, _r, _s) == _owner # dev: invalid signature self.nonces[_owner] = unsafe_add(nonce, 1) # <-- Unsafe add is safe here. self._approve(_owner, _spender, _value) return True @internal def mint(_to: address, _value: uint256) -> bool: """ @dev Mint an amount of the token and assigns it to an account. This encapsulates the modification of balances such that the proper events are emitted. @param _to The account that will receive the created tokens. @param _value The amount that will be created. @return bool Success. """ self.totalSupply += _value self.balanceOf[_to] += _value log Transfer(empty(address), _to, _value) return True @internal def mint_relative(_to: address, frac: uint256) -> uint256: """ @dev Increases supply by factor of (1 + frac/1e18) and mints it for _to @param _to The account that will receive the created tokens. @param frac The fraction of the current supply to mint. @return uint256 Amount of tokens minted. """ supply: uint256 = self.totalSupply d_supply: uint256 = supply * frac / 10**18 if d_supply > 0: self.totalSupply = supply + d_supply self.balanceOf[_to] += d_supply log Transfer(empty(address), _to, d_supply) return d_supply @internal def burnFrom(_to: address, _value: uint256) -> bool: """ @dev Burn an amount of the token from a given account. @param _to The account whose tokens will be burned. @param _value The amount that will be burned. @return bool Success. """ self.totalSupply -= _value self.balanceOf[_to] -= _value log Transfer(_to, empty(address), _value) return True # ------------------------- AMM View Functions ------------------------------- @external @view def fee_receiver() -> address: """ @notice Returns the address of the admin fee receiver. @return address Fee receiver. """ return Factory(self.factory).fee_receiver() @external @view def calc_token_amount(amounts: uint256[N_COINS], deposit: bool) -> uint256: """ @notice Calculate LP tokens minted or to be burned for depositing or removing `amounts` of coins @dev Includes fee. @param amounts Amounts of tokens being deposited or withdrawn @param deposit True if it is a deposit action, False if withdrawn. @return uint256 Amount of LP tokens deposited or withdrawn. """ view_contract: address = Factory(self.factory).views_implementation() return Views(view_contract).calc_token_amount(amounts, deposit, self) @external @view def get_dy(i: uint256, j: uint256, dx: uint256) -> uint256: """ @notice Get amount of coin[j] tokens received for swapping in dx amount of coin[i] @dev Includes fee. @param i index of input token. Check pool.coins(i) to get coin address at ith index @param j index of output token @param dx amount of input coin[i] tokens @return uint256 Exact amount of output j tokens for dx amount of i input tokens. """ view_contract: address = Factory(self.factory).views_implementation() return Views(view_contract).get_dy(i, j, dx, self) @external @view def get_dx(i: uint256, j: uint256, dy: uint256) -> uint256: """ @notice Get amount of coin[i] tokens to input for swapping out dy amount of coin[j] @dev This is an approximate method, and returns estimates close to the input amount. Expensive to call on-chain. @param i index of input token. Check pool.coins(i) to get coin address at ith index @param j index of output token @param dy amount of input coin[j] tokens received @return uint256 Approximate amount of input i tokens to get dy amount of j tokens. """ view_contract: address = Factory(self.factory).views_implementation() return Views(view_contract).get_dx(i, j, dy, self) @external @view @nonreentrant("lock") def lp_price() -> uint256: """ @notice Calculates the current price of the LP token w.r.t coin at the 0th index @return uint256 LP price. """ price_oracle: uint256[N_COINS-1] = self._unpack_prices( self.price_oracle_packed ) return ( 3 * self.virtual_price * MATH.cbrt(price_oracle[0] * price_oracle[1]) ) / 10**24 @external @view @nonreentrant("lock") def get_virtual_price() -> uint256: """ @notice Calculates the current virtual price of the pool LP token. @dev Not to be confused with `self.virtual_price` which is a cached virtual price. @return uint256 Virtual Price. """ return 10**18 * self.get_xcp(self.D) / self.totalSupply @external @view @nonreentrant("lock") def price_oracle(k: uint256) -> uint256: """ @notice Returns the oracle price of the coin at index `k` w.r.t the coin at index 0. @dev The oracle is an exponential moving average, with a periodicity determined by `self.ma_time`. The aggregated prices are cached state prices (dy/dx) calculated AFTER the latest trade. @param k The index of the coin. @return uint256 Price oracle value of kth coin. """ price_oracle: uint256 = self._unpack_prices(self.price_oracle_packed)[k] price_scale: uint256 = self._unpack_prices(self.price_scale_packed)[k] last_prices_timestamp: uint256 = self.last_prices_timestamp if last_prices_timestamp < block.timestamp: # <------------ Update moving # average if needed. last_prices: uint256 = self._unpack_prices(self.last_prices_packed)[k] ma_time: uint256 = self._unpack(self.packed_rebalancing_params)[2] alpha: uint256 = MATH.wad_exp( -convert( (block.timestamp - last_prices_timestamp) * 10**18 / ma_time, int256, ) ) # ---- We cap state price that goes into the EMA with 2 x price_scale. return ( min(last_prices, 2 * price_scale) * (10**18 - alpha) + price_oracle * alpha ) / 10**18 return price_oracle @external @view def last_prices(k: uint256) -> uint256: """ @notice Returns last price of the coin at index `k` w.r.t the coin at index 0. @dev last_prices returns the quote by the AMM for an infinitesimally small swap after the last trade. It is not equivalent to the last traded price, and is computed by taking the partial differential of `x` w.r.t `y`. The derivative is calculated in `get_p` and then multiplied with price_scale to give last_prices. @param k The index of the coin. @return uint256 Last logged price of coin. """ return self._unpack_prices(self.last_prices_packed)[k] @external @view def price_scale(k: uint256) -> uint256: """ @notice Returns the price scale of the coin at index `k` w.r.t the coin at index 0. @dev Price scale determines the price band around which liquidity is concentrated. @param k The index of the coin. @return uint256 Price scale of coin. """ return self._unpack_prices(self.price_scale_packed)[k] @external @view def fee() -> uint256: """ @notice Returns the fee charged by the pool at current state. @dev Not to be confused with the fee charged at liquidity action, since there the fee is calculated on `xp` AFTER liquidity is added or removed. @return uint256 fee bps. """ return self._fee(self.xp()) @view @external def calc_withdraw_one_coin(token_amount: uint256, i: uint256) -> uint256: """ @notice Calculates output tokens with fee @param token_amount LP Token amount to burn @param i token in which liquidity is withdrawn @return uint256 Amount of ith tokens received for burning token_amount LP tokens. """ return self._calc_withdraw_one_coin( self._A_gamma(), token_amount, i, (self.future_A_gamma_time > block.timestamp) )[0] @external @view def calc_token_fee( amounts: uint256[N_COINS], xp: uint256[N_COINS] ) -> uint256: """ @notice Returns the fee charged on the given amounts for add_liquidity. @param amounts The amounts of coins being added to the pool. @param xp The current balances of the pool multiplied by coin precisions. @return uint256 Fee charged. """ return self._calc_token_fee(amounts, xp) @view @external def A() -> uint256: """ @notice Returns the current pool amplification parameter. @return uint256 A param. """ return self._A_gamma()[0] @view @external def gamma() -> uint256: """ @notice Returns the current pool gamma parameter. @return uint256 gamma param. """ return self._A_gamma()[1] @view @external def mid_fee() -> uint256: """ @notice Returns the current mid fee @return uint256 mid_fee value. """ return self._unpack(self.packed_fee_params)[0] @view @external def out_fee() -> uint256: """ @notice Returns the current out fee @return uint256 out_fee value. """ return self._unpack(self.packed_fee_params)[1] @view @external def fee_gamma() -> uint256: """ @notice Returns the current fee gamma @return uint256 fee_gamma value. """ return self._unpack(self.packed_fee_params)[2] @view @external def allowed_extra_profit() -> uint256: """ @notice Returns the current allowed extra profit @return uint256 allowed_extra_profit value. """ return self._unpack(self.packed_rebalancing_params)[0] @view @external def adjustment_step() -> uint256: """ @notice Returns the current adjustment step @return uint256 adjustment_step value. """ return self._unpack(self.packed_rebalancing_params)[1] @view @external def ma_time() -> uint256: """ @notice Returns the current moving average time in seconds @dev To get time in seconds, the parameter is multipled by ln(2) One can expect off-by-one errors here. @return uint256 ma_time value. """ return self._unpack(self.packed_rebalancing_params)[2] * 694 / 1000 @view @external def precisions() -> uint256[N_COINS]: # <-------------- For by view contract. """ @notice Returns the precisions of each coin in the pool. @return uint256[3] precisions of coins. """ return self._unpack(self.packed_precisions) @external @view def fee_calc(xp: uint256[N_COINS]) -> uint256: # <----- For by view contract. """ @notice Returns the fee charged by the pool at current state. @param xp The current balances of the pool multiplied by coin precisions. @return uint256 Fee value. """ return self._fee(xp) @view @external def DOMAIN_SEPARATOR() -> bytes32: """ @notice EIP712 domain separator. @return bytes32 Domain Separator set for the current chain. """ return self._domain_separator() # ------------------------- AMM Admin Functions ------------------------------ @external def ramp_A_gamma( future_A: uint256, future_gamma: uint256, future_time: uint256 ): """ @notice Initialise Ramping A and gamma parameter values linearly. @dev Only accessible by factory admin, and only @param future_A The future A value. @param future_gamma The future gamma value. @param future_time The timestamp at which the ramping will end. """ assert msg.sender == Factory(self.factory).admin() # dev: only owner assert block.timestamp > self.initial_A_gamma_time + (MIN_RAMP_TIME - 1) # dev: ramp undergoing assert future_time > block.timestamp + MIN_RAMP_TIME - 1 # dev: insufficient time A_gamma: uint256[2] = self._A_gamma() initial_A_gamma: uint256 = A_gamma[0] << 128 initial_A_gamma = initial_A_gamma | A_gamma[1] assert future_A > MIN_A - 1 assert future_A < MAX_A + 1 assert future_gamma > MIN_GAMMA - 1 assert future_gamma < MAX_GAMMA + 1 ratio: uint256 = 10**18 * future_A / A_gamma[0] assert ratio < 10**18 * MAX_A_CHANGE + 1 assert ratio > 10**18 / MAX_A_CHANGE - 1 ratio = 10**18 * future_gamma / A_gamma[1] assert ratio < 10**18 * MAX_A_CHANGE + 1 assert ratio > 10**18 / MAX_A_CHANGE - 1 self.initial_A_gamma = initial_A_gamma self.initial_A_gamma_time = block.timestamp future_A_gamma: uint256 = future_A << 128 future_A_gamma = future_A_gamma | future_gamma self.future_A_gamma_time = future_time self.future_A_gamma = future_A_gamma log RampAgamma( A_gamma[0], future_A, A_gamma[1], future_gamma, block.timestamp, future_time, ) @external def stop_ramp_A_gamma(): """ @notice Stop Ramping A and gamma parameters immediately. @dev Only accessible by factory admin. """ assert msg.sender == Factory(self.factory).admin() # dev: only owner A_gamma: uint256[2] = self._A_gamma() current_A_gamma: uint256 = A_gamma[0] << 128 current_A_gamma = current_A_gamma | A_gamma[1] self.initial_A_gamma = current_A_gamma self.future_A_gamma = current_A_gamma self.initial_A_gamma_time = block.timestamp self.future_A_gamma_time = block.timestamp # ------ Now (block.timestamp < t1) is always False, so we return saved A. log StopRampA(A_gamma[0], A_gamma[1], block.timestamp) @external def commit_new_parameters( _new_mid_fee: uint256, _new_out_fee: uint256, _new_fee_gamma: uint256, _new_allowed_extra_profit: uint256, _new_adjustment_step: uint256, _new_ma_time: uint256, ): """ @notice Commit new parameters. @dev Only accessible by factory admin. @param _new_mid_fee The new mid fee. @param _new_out_fee The new out fee. @param _new_fee_gamma The new fee gamma. @param _new_allowed_extra_profit The new allowed extra profit. @param _new_adjustment_step The new adjustment step. @param _new_ma_time The new ma time. ma_time is time_in_seconds/ln(2). """ assert msg.sender == Factory(self.factory).admin() # dev: only owner assert self.admin_actions_deadline == 0 # dev: active action _deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY self.admin_actions_deadline = _deadline # ----------------------------- Set fee params --------------------------- new_mid_fee: uint256 = _new_mid_fee new_out_fee: uint256 = _new_out_fee new_fee_gamma: uint256 = _new_fee_gamma current_fee_params: uint256[3] = self._unpack(self.packed_fee_params) if new_out_fee < MAX_FEE + 1: assert new_out_fee > MIN_FEE - 1 # dev: fee is out of range else: new_out_fee = current_fee_params[1] if new_mid_fee > MAX_FEE: new_mid_fee = current_fee_params[0] assert new_mid_fee <= new_out_fee # dev: mid-fee is too high if new_fee_gamma < 10**18: assert new_fee_gamma > 0 # dev: fee_gamma out of range [1 .. 10**18] else: new_fee_gamma = current_fee_params[2] self.future_packed_fee_params = self._pack( [new_mid_fee, new_out_fee, new_fee_gamma] ) # ----------------- Set liquidity rebalancing parameters ----------------- new_allowed_extra_profit: uint256 = _new_allowed_extra_profit new_adjustment_step: uint256 = _new_adjustment_step new_ma_time: uint256 = _new_ma_time current_rebalancing_params: uint256[3] = self._unpack(self.packed_rebalancing_params) if new_allowed_extra_profit > 10**18: new_allowed_extra_profit = current_rebalancing_params[0] if new_adjustment_step > 10**18: new_adjustment_step = current_rebalancing_params[1] if new_ma_time < 872542: # <----- Calculated as: 7 * 24 * 60 * 60 / ln(2) assert new_ma_time > 86 # dev: MA time should be longer than 60/ln(2) else: new_ma_time = current_rebalancing_params[2] self.future_packed_rebalancing_params = self._pack( [new_allowed_extra_profit, new_adjustment_step, new_ma_time] ) # ---------------------------------- LOG --------------------------------- log CommitNewParameters( _deadline, new_mid_fee, new_out_fee, new_fee_gamma, new_allowed_extra_profit, new_adjustment_step, new_ma_time, ) @external @nonreentrant("lock") def apply_new_parameters(): """ @notice Apply committed parameters. @dev Only callable after admin_actions_deadline. """ 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 packed_fee_params: uint256 = self.future_packed_fee_params self.packed_fee_params = packed_fee_params packed_rebalancing_params: uint256 = self.future_packed_rebalancing_params self.packed_rebalancing_params = packed_rebalancing_params rebalancing_params: uint256[3] = self._unpack(packed_rebalancing_params) fee_params: uint256[3] = self._unpack(packed_fee_params) log NewParameters( fee_params[0], fee_params[1], fee_params[2], rebalancing_params[0], rebalancing_params[1], rebalancing_params[2], ) @external def revert_new_parameters(): """ @notice Revert committed parameters @dev Only accessible by factory admin. Setting admin_actions_deadline to 0 ensures a revert in apply_new_parameters. """ assert msg.sender == Factory(self.factory).admin() # dev: only owner self.admin_actions_deadline = 0
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"name":"Transfer","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"receiver","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"uint256","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"uint256","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false},{"name":"fee","type":"uint256","indexed":false},{"name":"packed_price_scale","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[3]","indexed":false},{"name":"fee","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false},{"name":"packed_price_scale","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[3]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_index","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false},{"name":"approx_fee","type":"uint256","indexed":false},{"name":"packed_price_scale","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"CommitNewParameters","inputs":[{"name":"deadline","type":"uint256","indexed":true},{"name":"mid_fee","type":"uint256","indexed":false},{"name":"out_fee","type":"uint256","indexed":false},{"name":"fee_gamma","type":"uint256","indexed":false},{"name":"allowed_extra_profit","type":"uint256","indexed":false},{"name":"adjustment_step","type":"uint256","indexed":false},{"name":"ma_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"NewParameters","inputs":[{"name":"mid_fee","type":"uint256","indexed":false},{"name":"out_fee","type":"uint256","indexed":false},{"name":"fee_gamma","type":"uint256","indexed":false},{"name":"allowed_extra_profit","type":"uint256","indexed":false},{"name":"adjustment_step","type":"uint256","indexed":false},{"name":"ma_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RampAgamma","inputs":[{"name":"initial_A","type":"uint256","indexed":false},{"name":"future_A","type":"uint256","indexed":false},{"name":"initial_gamma","type":"uint256","indexed":false},{"name":"future_gamma","type":"uint256","indexed":false},{"name":"initial_time","type":"uint256","indexed":false},{"name":"future_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StopRampA","inputs":[{"name":"current_A","type":"uint256","indexed":false},{"name":"current_gamma","type":"uint256","indexed":false},{"name":"time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ClaimAdminFee","inputs":[{"name":"admin","type":"address","indexed":true},{"name":"tokens","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_coins","type":"address[3]"},{"name":"_math","type":"address"},{"name":"_weth","type":"address"},{"name":"_salt","type":"bytes32"},{"name":"packed_precisions","type":"uint256"},{"name":"packed_A_gamma","type":"uint256"},{"name":"packed_fee_params","type":"uint256"},{"name":"packed_rebalancing_params","type":"uint256"},{"name":"packed_prices","type":"uint256"}],"outputs":[]},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"payable","type":"function","name":"exchange","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"exchange","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"use_eth","type":"bool"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"exchange","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"use_eth","type":"bool"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"exchange_underlying","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"exchange_underlying","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_extended","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"use_eth","type":"bool"},{"name":"sender","type":"address"},{"name":"receiver","type":"address"},{"name":"cb","type":"bytes32"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"add_liquidity","inputs":[{"name":"amounts","type":"uint256[3]"},{"name":"min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"add_liquidity","inputs":[{"name":"amounts","type":"uint256[3]"},{"name":"min_mint_amount","type":"uint256"},{"name":"use_eth","type":"bool"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"add_liquidity","inputs":[{"name":"amounts","type":"uint256[3]"},{"name":"min_mint_amount","type":"uint256"},{"name":"use_eth","type":"bool"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[3]"}],"outputs":[{"name":"","type":"uint256[3]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[3]"},{"name":"use_eth","type":"bool"}],"outputs":[{"name":"","type":"uint256[3]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[3]"},{"name":"use_eth","type":"bool"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256[3]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[3]"},{"name":"use_eth","type":"bool"},{"name":"receiver","type":"address"},{"name":"claim_admin_fees","type":"bool"}],"outputs":[{"name":"","type":"uint256[3]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"},{"name":"use_eth","type":"bool"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"},{"name":"use_eth","type":"bool"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"claim_admin_fees","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"transferFrom","inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"transfer","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"approve","inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"increaseAllowance","inputs":[{"name":"_spender","type":"address"},{"name":"_add_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"decreaseAllowance","inputs":[{"name":"_spender","type":"address"},{"name":"_sub_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"permit","inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_deadline","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"fee_receiver","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"amounts","type":"uint256[3]"},{"name":"deposit","type":"bool"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dx","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"lp_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_oracle","inputs":[{"name":"k","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"last_prices","inputs":[{"name":"k","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_scale","inputs":[{"name":"k","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_withdraw_one_coin","inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_token_fee","inputs":[{"name":"amounts","type":"uint256[3]"},{"name":"xp","type":"uint256[3]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"mid_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"out_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"fee_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"allowed_extra_profit","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"adjustment_step","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"precisions","inputs":[],"outputs":[{"name":"","type":"uint256[3]"}]},{"stateMutability":"view","type":"function","name":"fee_calc","inputs":[{"name":"xp","type":"uint256[3]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"DOMAIN_SEPARATOR","inputs":[],"outputs":[{"name":"","type":"bytes32"}]},{"stateMutability":"nonpayable","type":"function","name":"ramp_A_gamma","inputs":[{"name":"future_A","type":"uint256"},{"name":"future_gamma","type":"uint256"},{"name":"future_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"stop_ramp_A_gamma","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"commit_new_parameters","inputs":[{"name":"_new_mid_fee","type":"uint256"},{"name":"_new_out_fee","type":"uint256"},{"name":"_new_fee_gamma","type":"uint256"},{"name":"_new_allowed_extra_profit","type":"uint256"},{"name":"_new_adjustment_step","type":"uint256"},{"name":"_new_ma_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"apply_new_parameters","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"revert_new_parameters","inputs":[],"outputs":[]},{"stateMutability":"view","type":"function","name":"WETH20","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"MATH","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"coins","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"factory","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"last_prices_timestamp","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A_gamma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A_gamma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"balances","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"D","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"xcp_profit","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"xcp_profit_a","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"packed_rebalancing_params","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"packed_fee_params","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ADMIN_FEE","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"admin_actions_deadline","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"name","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8"}]},{"stateMutability":"view","type":"function","name":"version","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"allowance","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"nonces","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"salt","inputs":[],"outputs":[{"name":"","type":"bytes32"}]}]
Deployed Bytecode
0x6003361161000c576129bd565b5f3560e01c635b41b9088118610033576084361061534c575f610aa05233610ac052610091565b63394747c5811861005f5760a4361061534c576084358060011c61534c57610aa05233610ac052610091565b63ce7d650381186100dd5760c4361061534c576084358060011c61534c57610aa05260a4358060a01c61534c57610ac0525b5f5460021461534c5760025f556020336105e05234610600526080600461062037610aa0516106a052610ac0516106c0526040366106e0376100d4610ae0613cbd565b610ae060035f55f35b6365b2489b81186100fa576084361061534c5733610aa05261011d565b63e2ad025a81186101675760a4361061534c576084358060a01c61534c57610aa0525b5f5460021461534c5760025f556020336105e0523461060052608060046106203760016106a052610aa0516106c0526040366106e03761015e610ac0613cbd565b610ac060035f55f35b634515cef38118610189576084361061534c575f6105e05233610600526101e7565b632b6e993a81186101b55760a4361061534c576084358060011c61534c576105e05233610600526101e7565b635cecb5f781186109a75760c4361061534c576084358060011c61534c576105e05260a4358060a01c61534c57610600525b5f5460021461534c5760025f556101ff610660612d41565b61066080516106205260208101516106405250600b5461066052600c5461068052600d546106a052610120366106c03760043560243580820182811061534c579050905060443580820182811061534c57905090501561534c5760015460405261026a610840612e9a565b61084080516107e05260208101516108005260408101516108205250600354610840526108405160405261029f6108a0612ed6565b6108a080516108605260208101516108805250610660516108a052610680516108c0526106a0516108e0525f6003905b8061090052610900516002811161534c5760051b6106600151610900516002811161534c5760051b6004013580820182811061534c57905090506109205261092051610900516002811161534c5760051b610660015261092051610900516002811161534c57600b01556001018181186102cf575050610660516107205261068051610740526106a05161076052610660516107e05180820281158383830414171561534c5790509050610660526108a0516107e05180820281158383830414171561534c57905090506108a052600160028101905b8061090052670de0b6b3a7640000610900516002811161534c5760051b6106600151610900516001810381811161534c5790506001811161534c5760051b610860015180820281158383830414171561534c5790509050610900516002811161534c5760051b6107e0015180820281158383830414171561534c579050905004610900516002811161534c5760051b6106600152670de0b6b3a7640000610900516002811161534c5760051b6108a00151610900516002811161534c5760051b6107e00151610900516001810381811161534c5790506001811161534c5760051b61086001510280820281158383830414171561534c579050905004610900516002811161534c5760051b6108a001526001018181186103a55750505f6003905b8061090052610900516002811161534c5760051b600401351561060457602061535d5f395f516020610900516002811161534c5760051b60400161535d015f395f511861056a576020610900516002811161534c5760051b60400161535d015f395f51604052610900516002811161534c5760051b600401356060525f6080523460a05260403660c03733610100525f610120526105e051610140526105b9612a10565b6020610900516002811161534c5760051b60400161535d015f395f51604052610900516002811161534c5760051b600401356060526080366080373361010052604036610120376105b9612a10565b610900516002811161534c5760051b6106600151610900516002811161534c5760051b6108a0015180820382811161534c5790509050610900516002811161534c5760051b6106c001525b6001018181186104c657505042600a541161062557600e546107c052610690565b602061537d5f395f51637b12e00961090052610620516109205261064051610940526108a051610960526108c051610980526108e0516109a0525f6109c052602061090060c461091c845afa61067d573d5f5f3e3d5ffd5b60203d1061534c576109009050516107c0525b602061537d5f395f51637b12e00961092052610620516109405261064051610960526106605161098052610680516109a0526106a0516109c0525f6109e052602061092060c461093c845afa6106e8573d5f5f3e3d5ffd5b60203d1061534c5761092090505161090052601954610920526107c0511561075257610920516109005180820281158383830414171561534c57905090506107c051801561534c57808204905090506109205180820382811161534c57905090506107805261076d565b6109005160405261076461094061444a565b61094051610780525b610780511561534c576107c0511561088e576106c0516101e0526106e051610200526107005161022052610660516102405261068051610260526106a051610280526107ba610940614537565b610940516107805180820281158383830414171561534c57905090506402540be400810490506001810181811061534c5790506107a052610780516107a05180820382811161534c579050905061078052610920516107805180820182811061534c579050905061092052610600516040526107805160605261083e610940614662565b610940506106205160e0526106405161010052610660516101205261068051610140526106a0516101605261090051610180525f6101a05261088161094061309d565b61094051610840526108d7565b61090051600e55670de0b6b3a7640000601155670de0b6b3a7640000600f55670de0b6b3a764000060105561060051604052610780516060526108d2610940614662565b610940505b606435610780511015610949576008610940527f536c6970706167650000000000000000000000000000000000000000000000006109605261094050610940518061096001601f825f031636823750506308c379a061090052602061092052601f19601f61094051011660440161091cfd5b610600517fe1b60455bd9e33720b547f60e4e0cfbf1252d0f2ee0147d53029945f39fe3c1a60606004610940376107a0516109a052610920516109c052610840516109e05260c0610940a261099c6148a6565b602061078060035f55f35b6317e26cd181186109c9573461534c57602061535d5f395f5160405260206040f35b63ed6c154681186109eb573461534c57602061537d5f395f5160405260206040f35b63c66106578118610a27576024361061534c573461534c5760206004356002811161534c5760051b60400161535d015f395f5160405260206040f35b63c45a01558118610a43573461534c5760025460405260206040f35b636112c7478118610a5f573461534c5760065460405260206040f35b63204fe3d58118610a7b573461534c5760075460405260206040f35b63e89876ff8118610a97573461534c5760085460405260206040f35b63f30cfad58118610ab3573461534c5760095460405260206040f35b63f9ed95978118610acf573461534c57600a5460405260206040f35b634903b0d18118610aff576024361061534c573461534c576004356002811161534c57600b015460405260206040f35b630f529ba28118610b1b573461534c57600e5460405260206040f35b637ba1a74d8118610b37573461534c57600f5460405260206040f35b630b7b594b8118610b53573461534c5760105460405260206040f35b630c46b72a8118610b6f573461534c5760115460405260206040f35b633dd654788118610b8b573461534c5760125460405260206040f35b63e36164058118610ba7573461534c5760145460405260206040f35b634469ed148118610bc6573461534c5764012a05f20060405260206040f35b63405e28f88118610be2573461534c5760165460405260206040f35b6306fdde038118610c3d573461534c576020806040528060400160206153fd5f395f51602082018161541d823950808252508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b6395d89b418118610c9d573461534c5760208060405280604001602061545d5f395f5160208201602061547d5f395f51815250808252508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63313ce5678118610cb8573461534c57601260405260206040f35b6354fd4d508118610d3c573461534c5760208060805260066040527f76322e302e30000000000000000000000000000000000000000000000000000060605260408160800181516020830160208301815181525050808252508051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b6370a082318118610d7a576024361061534c576004358060a01c61534c576040523461534c5760176040516020525f5260405f205460605260206060f35b63dd62ed3e8118610dd5576044361061534c576004358060a01c61534c576040526024358060a01c61534c576060523461534c5760186040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6318160ddd8118610df1573461534c5760195460405260206040f35b637ecebe008118610e2f576024361061534c576004358060a01c61534c576040523461534c57601a6040516020525f5260405f205460605260206060f35b63bfa0b1338118610e51573461534c5760206154dd5f395f5160405260206040f35b63dd96994f8118610ef357610104361061534c576084358060011c61534c57610aa05260a4358060a01c61534c57610ac05260c4358060a01c61534c57610ae0523461534c575f5460021461534c5760025f5560e4351561534c576020610ac0516105e0525f610600526080600461062037610aa0516106a052610ae0516106c052336106e05260e43561070052610eea610b00613cbd565b610b0060035f55f35b63ecb586a58118610f1b576084361061534c575f610400523361042052600161044052610fca565b63fce647368118610f4d5760a4361061534c576084358060011c61534c57610400523361042052600161044052610fca565b631da3d2388118610f895760c4361061534c576084358060011c61534c576104005260a4358060a01c61534c5761042052600161044052610fca565b635cd3478081186112ab5760e4361061534c576084358060011c61534c576104005260a4358060a01c61534c576104205260c4358060011c61534c57610440525b3461534c575f5460021461534c5760025f5560043561046052600b5461048052600c546104a052600d546104c0526060366104e0376104405115611010576110106148a6565b601954610540523360405260043560605261102c610560614c09565b61056050610540516104605118611091575f6003905b8061056052610560516002811161534c5760051b6104800151610560516002811161534c5760051b6104e001525f610560516002811161534c57600b01556001018181186110425750506111a8565b610460516001810381811161534c579050610460525f6003905b8061056052610560516002811161534c5760051b61048001516104605180820281158383830414171561534c579050905061054051801561534c5780820490509050610560516002811161534c5760051b6104e00152610560516002811161534c5760051b60240135610560516002811161534c5760051b6104e001511061534c57610560516002811161534c5760051b6104800151610560516002811161534c5760051b6104e0015180820382811161534c5790509050610560516002811161534c57600b0155610560516002811161534c5760051b6104e00151610560516002811161534c5760051b61048001526001018181186110ab5750505b600e54610560526105605161054051610560516104605180820281158383830414171561534c57905090500480820382811161534c5790509050600e555f6003905b80610580526020610580516002811161534c5760051b60400161535d015f395f51604052610580516002811161534c5760051b6104e00151606052610400516080526104205160a05261123b612c4a565b6001018181186111ea575050337fd6cc314a0b1e3b2579f8e64248e82434072e8271290eef8ad0886709304195f561048051610580526104a0516105a0526104c0516105c0526105405160043580820382811161534c57905090506105e0526080610580a260606104e060035f55f35b63f1dc3cc981186112cd576064361061534c575f61066052336106805261132b565b638f15b6b581186112f9576084361061534c576064358060011c61534c5761066052336106805261132b565b6307329bcd81186115545760a4361061534c576064358060011c61534c57610660526084358060a01c61534c57610680525b3461534c575f5460021461534c5760025f556113486106e0612d41565b6106e080516106a05260208101516106c0525060e0366106e03761136a6148a6565b6106a0516101e0526106c05161020052604060046102203742600a5411610260526113966107c0614c7b565b6107c080516106e052602081015161070052604081018051610740526020810151610760526040810151610780525060a08101516107a052506044356106e05110156114415760086107c0527f536c6970706167650000000000000000000000000000000000000000000000006107e0526107c0506107c051806107e001601f825f031636823750506308c379a06107805260206107a052601f19601f6107c051011660440161079cfd5b6024356002811161534c57600b0180546106e05180820382811161534c57905090508155503360405260043560605261147b6107c0614c09565b6107c05060206024356002811161534c5760051b60400161535d015f395f516040526106e051606052610660516080526106805160a0526114ba612c4a565b6106a05160e0526106c0516101005261074051610120526107605161014052610780516101605261070051610180525f6101a0526114f96107e061309d565b6107e0516107c052337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c604060046107e0376106e051610820526107a051610840526107c0516108605260a06107e0a260206106e060035f55f35b63c93f49e8811861157f573461534c575f5460021461534c5760025f556115796148a6565b60035f55005b6323b872dd811861164e576064361061534c576004358060a01c61534c5760c0526024358060a01c61534c5760e0523461534c57601860c0516020525f5260405f2080336020525f5260405f20905054610100527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010051146116275760c051604052336060526101005160443580820382811161534c57905090506080526116276151c1565b60c05160405260e051606052604435608052611641615216565b6001610120526020610120f35b63a9059cbb8118611697576044361061534c576004358060a01c61534c5760c0523461534c573360405260c05160605260243560805261168c615216565b600160e052602060e0f35b63095ea7b381186116e0576044361061534c576004358060a01c61534c5760c0523461534c573360405260c0516060526024356080526116d56151c1565b600160e052602060e0f35b63395093518118611795576044361061534c576004358060a01c61534c5760c0523461534c576018336020525f5260405f208060c0516020525f5260405f2090505460e05260243560e051016101005260e051610100511015611763577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100525b60e0516101005114611788573360405260c051606052610100516080526117886151c1565b6001610120526020610120f35b63a457c2d7811861182a576044361061534c576004358060a01c61534c5760c0523461534c576018336020525f5260405f208060c0516020525f5260405f2090505460e05260243560e05103610100526101005160e05110156117f8575f610100525b60e051610100511461181d573360405260c0516060526101005160805261181d6151c1565b6001610120526020610120f35b63d505accf81186119e75760e4361061534c576004358060a01c61534c57610120526024358060a01c61534c57610140526084358060081c61534c57610160523461534c57610120511561534c57606435421161534c57601a610120516020525f5260405f2054610180525f60026101c0527f19010000000000000000000000000000000000000000000000000000000000006101e0526101c08051602082018361032001815181525050808301925050506118e76102006152ad565b610200518161032001526020810190507f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c961024052610120516102605261014051610280526044356102a052610180516102c0526064356102e05260c061022052610220805160208201209050816103200152602081019050806103005261030090508051602082012090506101a052610120516101a0516101c052610160516101e052604060a46102003760205f60806101c060015afa505f511861534c5760016101805101601a610120516020525f5260405f205561012051604052610140516060526044356080526119da6151c1565b60016101c05260206101c0f35b63cab4d3db8118611a37573461534c57602060025463cab4d3db604052602060406004605c845afa611a1b573d5f5f3e3d5ffd5b60203d1061534c576040518060a01c61534c5760805260809050f35b633883e1198118611ae2576084361061534c576064358060011c61534c576040523461534c5760025463e31593d8608052602060806004609c845afa611a7f573d5f5f3e3d5ffd5b60203d1061534c576080518060a01c61534c5760c05260c09050516060526020606051638585c4b16080526060600460a0376040516101005230610120526020608060a4609c845afa611ad4573d5f5f3e3d5ffd5b60203d1061534c5760809050f35b63556d6e9f8118611b77576064361061534c573461534c5760025463e31593d8606052602060606004607c845afa611b1c573d5f5f3e3d5ffd5b60203d1061534c576060518060a01c61534c5760a05260a09050516040526020604051633bb1f8c1606052606060046080373060e052602060606084607c845afa611b69573d5f5f3e3d5ffd5b60203d1061534c5760609050f35b6337ed3a7a8118611c0c576064361061534c573461534c5760025463e31593d8606052602060606004607c845afa611bb1573d5f5f3e3d5ffd5b60203d1061534c576060518060a01c61534c5760a05260a090505160405260206040516399bf0b76606052606060046080373060e052602060606084607c845afa611bfe573d5f5f3e3d5ffd5b60203d1061534c5760609050f35b6354f0f7d58118611ce1573461534c575f5460021461534c57600454604052611c36610120612ed6565b610120805160e052602081015161010052506011546003810281600382041861534c579050602061537d5f395f5163f42c56c26101205260e0516101005180820281158383830414171561534c5790509050610140526020610120602461013c845afa611ca5573d5f5f3e3d5ffd5b60203d1061534c5761012090505180820281158383830414171561534c579050905069d3c21bcecceda100000081049050610160526020610160f35b63bb7b8b808118611d48573461534c575f5460021461534c57600e54604052611d0b61016061444a565b61016051670de0b6b3a7640000810281670de0b6b3a764000082041861534c579050601954801561534c5780820490509050610180526020610180f35b63687276538118611f72576024361061534c573461534c575f5460021461534c57600454604052611d7a610100612ed6565b6101006004356001811161534c5760051b810190505160e052600354604052611da4610120612ed6565b6101206004356001811161534c5760051b8101905051610100526006546101205242610120511015611f6b57600554604052611de1610160612ed6565b6101606004356001811161534c5760051b810190505161014052601254604052611e0c610180612e9a565b6101806040810190505161016052602061537d5f395f516381d18d876101a052426101205180820382811161534c5790509050670de0b6b3a7640000810281670de0b6b3a764000082041861534c57905061016051801561534c57808204905090508060ff1c61534c577f8000000000000000000000000000000000000000000000000000000000000000811461534c575f036101c05260206101a060246101bc845afa611ebc573d5f5f3e3d5ffd5b60203d1061534c576101a09050516101805261014051610100518060011b818160011c1861534c579050808281188284100218905090506101805180670de0b6b3a764000003670de0b6b3a7640000811161534c57905080820281158383830414171561534c579050905060e0516101805180820281158383830414171561534c579050905080820182811061534c5790509050670de0b6b3a7640000810490506101a05260206101a0611f70565b602060e05bf35b63591890178118611fb2576024361061534c573461534c576020600554604052611f9c60e0612ed6565b60e06004356001811161534c5760051b81019050f35b63a3f7cdd58118611ff2576024361061534c573461534c576020600354604052611fdc60e0612ed6565b60e06004356001811161534c5760051b81019050f35b63ddca3f438118612050573461534c57602061200f6101e0614780565b6101e080516102605260208101516102805260408101516102a0525061026051606052610280516080526102a05160a05261204b610240612f37565b610240f35b634fb08c5e81186120d1576044361061534c573461534c576020612075610660612d41565b61066080516107605260208101516107805250604060046107a03742600a54116107e052610760516101e05261078051610200526107a051610220526107c051610240526107e051610260526120cc6106a0614c7b565b6106a0f35b63cde699fa811861210b5760c4361061534c573461534c576020606060046101e0376060606461024037612106610340614537565b610340f35b63f446c1d0811861212d573461534c576020612128610120612d41565b610120f35b63b13739298118612155573461534c57602061214a610120612d41565b610120602081019050f35b6392526c0c811861217b573461534c5760206014546040526121776060612e9a565b6060f35b63ee8de67581186121a7573461534c57602060145460405261219d6060612e9a565b6060602081019050f35b6372d4f0e281186121d3573461534c5760206014546040526121c96060612e9a565b6060604081019050f35b6349fe9e7781186121f9573461534c5760206012546040526121f56060612e9a565b6060f35b63083812e58118612225573461534c57602060125460405261221b6060612e9a565b6060602081019050f35b6309c3da6a8118612270573461534c576012546040526122456060612e9a565b6060604081019050516102b68102816102b682041861534c5790506103e88104905060c052602060c0f35b633620604b8118612296573461534c5760606001546040526122926060612e9a565b6060f35b63572e562581186122c7576064361061534c573461534c576020606060046060376122c26101e0612f37565b6101e0f35b633644e51581186122e9573461534c5760206122e46101206152ad565b610120f35b635e248072811861252a576064361061534c573461534c5760025463f851a440610120526020610120600461013c845afa612326573d5f5f3e3d5ffd5b60203d1061534c57610120518060a01c61534c5761016052610160905051331861534c576008546201517f810181811061534c57905042111561534c574262015180810181811061534c5790506001810381811161534c579050604435111561534c57612394610160612d41565b610160805161012052602081015161014052506101205160801b6101605261014051610160511761016052610a8c6004351061534c57631017df806004351161534c576402540be4006024351061534c5766b1a2bc2ec500006024351161534c57600435670de0b6b3a7640000810281670de0b6b3a764000082041861534c57905061012051801561534c578082049050905061018052678ac7230489e80000610180511161534c5767016345785d8a0000610180511061534c57602435670de0b6b3a7640000810281670de0b6b3a764000082041861534c57905061014051801561534c578082049050905061018052678ac7230489e80000610180511161534c5767016345785d8a0000610180511061534c57610160516007554260085560043560801b6101a0526024356101a051176101a052604435600a556101a0516009557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd05610120516101c0526004356101e05261014051610200526024356102205242610240526044356102605260c06101c0a1005b63244c7c2e811861260d573461534c5760025463f851a440610120526020610120600461013c845afa61255f573d5f5f3e3d5ffd5b60203d1061534c57610120518060a01c61534c5761016052610160905051331861534c5761258e610160612d41565b610160805161012052602081015161014052506101205160801b610160526101405161016051176101605261016051600755610160516009554260085542600a557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc2846101205161018052610140516101a052426101c0526060610180a1005b634711a4f881186128675760c4361061534c573461534c5760025463f851a44060a052602060a0600460bc845afa612647573d5f5f3e3d5ffd5b60203d1061534c5760a0518060a01c61534c5760e05260e0905051331861534c5760165461534c57426203f480810181811061534c57905060a05260a0516016556060600460c0376014546040526126a0610180612e9a565b610180805161012052602081015161014052604081015161016052506402540be40060e05111156126d7576101405160e0526126e4565b6207a12060e0511061534c575b6402540be40160c051106126fa576101205160c0525b60e05160c0511161534c57670de0b6b3a763ffff61010051111561272557610160516101005261272f565b610100511561534c575b60c05160405260e0516060526101005160805261274d6101806151ab565b61018051601555606060646101803760125460405261276d610240612e9a565b61024080516101e05260208101516102005260408101516102205250670de0b6b3a764000161018051106127a4576101e051610180525b670de0b6b3a76400016101a051106127bf57610200516101a0525b620d505d6101c05111156127da57610220516101c0526127e6565b60576101c0511061534c575b610180516040526101a0516060526101c0516080526128066102406151ab565b6102405160135560a0517fec36b92a482408f90e07357ca20c8cfaca85affe765903cb242e377fafb166af60c0516102405260e051610260526101005161028052610180516102a0526101a0516102c0526101c0516102e05260c0610240a2005b632a7dd7cd8118612962573461534c575f5460021461534c5760025f55601654421061534c576016541561534c575f6016556015546060526060516014556013546080526080516012556080516040526128c2610100612e9a565b610100805160a052602081015160c052604081015160e052506060516040526128ec610160612e9a565b610160805161010052602081015161012052604081015161014052507fa32137411fc7c20db359079cd84af0e2cad58cd7a182a8a5e23e08e554e88bf061010051610160526101205161018052610140516101a05260a0516101c05260c0516101e05260e0516102005260c0610160a160035f55005b63226840fb81186129bb573461534c5760025463f851a440604052602060406004605c845afa612994573d5f5f3e3d5ffd5b60203d1061534c576040518060a01c61534c576080526080905051331861534c575f601655005b505b3415612a0e57602061535d5f395f515f6040525f6003905b60208160051b60400161535d015f395f5183186129f6576001604052612a01565b6001018181186129d5575b505060405190501561534c575b005b61014051612a1e575f612a2d565b602061535d5f395f5160405118155b612c3c5760a05161534c5760e051612ab9576040516323b872dd610160526101005161018052306101a0526060516101c0526020610160606461017c5f855af1612a79573d5f5f3e3d5ffd5b3d612a9057803b1561534c5760016101e052612aa9565b60203d1061534c57610160518060011c61534c576101e0525b6101e09050511561534c57612bec565b6040516370a0823161018052306101a0526020610180602461019c845afa612ae3573d5f5f3e3d5ffd5b60203d1061534c57610180905051610160525f60e0516101805261018080516101c05260046101a0526101a09050805160208201836102c001815181525050808301925050506101005161020052610120516102205260405161024052606051610260526080516102805260a06101e0526101e0805160208201836102c0018281848460045afa50505080830192505050806102a0526102a050505f5f6102a0516102c05f60c0515af1612b99573d5f5f3e3d5ffd5b6060516040516370a0823161018052306101a0526020610180602461019c845afa612bc6573d5f5f3e3d5ffd5b60203d1061534c576101809050516101605180820382811161534c57905090501861534c575b602061535d5f395f5160405118612c4857602061535d5f395f51632e1a7d4d6101605260605161018052803b1561534c575f610160602461017c5f855af1612c36573d5f5f3e3d5ffd5b50612c48565b60605160a0511861534c575b565b608051612c57575f612c66565b602061535d5f395f5160405118155b612d1d57602061535d5f395f5160405118612cae57602061535d5f395f5163d0e30db060c052803b1561534c575f60c0600460dc606051855af1612cac573d5f5f3e3d5ffd5b505b60405163a9059cbb60c05260a05160e05260605161010052602060c0604460dc5f855af1612cde573d5f5f3e3d5ffd5b3d612cf557803b1561534c57600161012052612d0d565b60203d1061534c5760c0518060011c61534c57610120525b6101209050511561534c57612d3f565b5f60c05260c0505f5f60c05160e060605160a0515af1612d3f573d5f5f3e3d5ffd5b565b600a546040526009546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a052604051421015612e8a5760075460c05260085460e05260405160e05180820382811161534c57905090506040524260e05180820382811161534c579050905060e05260405160e05180820382811161534c57905090506101005260c05160801c6101005180820281158383830414171561534c579050905060a05160e05180820281158383830414171561534c579050905080820182811061534c5790509050604051801561534c578082049050905060a0526fffffffffffffffffffffffffffffffff60c051166101005180820281158383830414171561534c579050905060805160e05180820281158383830414171561534c579050905080820182811061534c5790509050604051801561534c57808204905090506080525b60a0518152608051602082015250565b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b60403660603760405160a0525f6002905b8060c0526fffffffffffffffffffffffffffffffff60a0511660c0516001811161534c5760051b6060015260a05160801c60a052600101818118612ee75750506060518152608051602082015250565b601454604052612f48610120612e9a565b610120805160c052602081015160e05260408101516101005250602061537d5f395f5163fa18042d61014052606051610160526080516101805260a0516101a052610100516101c0526020610140608461015c845afa612faa573d5f5f3e3d5ffd5b60203d1061534c5761014090505161012052670de0b6b3a764000060c0516101205180820281158383830414171561534c579050905060e0516101205180670de0b6b3a764000003670de0b6b3a7640000811161534c57905080820281158383830414171561534c579050905080820182811061534c579050905004815250565b6040366080375f6002905b8060c05260805160801b60805260c051806001036001811161534c5790506001811161534c5760051b6040015160a0526ffffffffffffffffffffffffffffffffe60a0511161534c5760805160a05117608052600101818118613036575050608051815250565b6012546040526130ae610220612e9a565b61022080516101c05260208101516101e052604081015161020052506004546040526130db610260612ed6565b610260805161022052602081015161024052506005546040526130ff6102a0612ed6565b6102a0805161026052602081015161028052506003546102a0526102a05160405261312b610300612ed6565b61030080516102c05260208101516102e0525060195461030052600f546103205260115461034052600654610360524261036051101561331957602061537d5f395f516381d18d876103a05261020051426103605180820382811161534c5790509050670de0b6b3a7640000810281670de0b6b3a764000082041861534c579050048060ff1c61534c577f8000000000000000000000000000000000000000000000000000000000000000811461534c575f036103c05260206103a060246103bc845afa6131fb573d5f5f3e3d5ffd5b60203d1061534c576103a0905051610380525f6002905b806103a052670de0b6b3a76400006103a0516001811161534c5760051b61026001516103a0516001811161534c5760051b6102c001518060011b818160011c1861534c579050808281188284100218905090506103805180670de0b6b3a764000003670de0b6b3a7640000811161534c57905080820281158383830414171561534c57905090506103a0516001811161534c5760051b61022001516103805180820281158383830414171561534c579050905080820182811061534c5790509050046103a0516001811161534c5760051b6102200152600101818118613212575050610220516040526102405160605261330d6103a061302b565b6103a051600455426006555b61018051610380526101805161339657602061537d5f395f51637b12e0096103a05260e0516103c052610100516103e0526101205161040052610140516104205261016051610440526101a0516104605260206103a060c46103bc845afa613383573d5f5f3e3d5ffd5b60203d1061534c576103a0905051610380525b602061537d5f395f5163754b76b36103a052610120516103c052610140516103e0526101605161040052610380516104205260e05161044052610100516104605260406103a060c46103bc845afa6133f0573d5f5f3e3d5ffd5b60403d1061534c576103a09050805161026052602081015161028052505f6002905b806103a052670de0b6b3a76400006103a0516001811161534c5760051b61026001516103a0516001811161534c5760051b6102c0015180820281158383830414171561534c5790509050046103a0516001811161534c5760051b610260015260010181811861341257505061026051604052610280516060526134966103a061302b565b6103a0516005556060366103a037600361038051046103a0525f6002905b806104005261038051670de0b6b3a7640000810281670de0b6b3a764000082041861534c579050610400516001811161534c5760051b6102c001516003810281600382041861534c579050801561534c5780820490509050610400516001810181811061534c5790506002811161534c5760051b6103a001526001018181186134b4575050670de0b6b3a764000061040052670de0b6b3a764000061042052610340511561368957602061537d5f395f5163bad1dc26610460526103a051610480526103c0516104a0526103e0516104c0526020610460606461047c845afa61359f573d5f5f3e3d5ffd5b60203d1061534c576104609050516104405261044051670de0b6b3a7640000810281670de0b6b3a764000082041861534c57905061030051801561534c57808204905090506104205261034051610320516104205180820281158383830414171561534c5790509050046104005242600a54101561368957610340516104205111613689576004610460527f4c6f7373000000000000000000000000000000000000000000000000000000006104805261046050610460518061048001601f825f031636823750506308c379a061042052602061044052601f19601f61046051011660440161043cfd5b61040051600f55610400516101c0518060011b818160011c1861534c57905080820182811061534c5790509050610420518060011b818160011c1861534c579050670de0b6b3a7640000810381811161534c5790501115613ca557604036610440375f6002905b8061048052610480516001811161534c5760051b6102c00151610480516001811161534c5760051b6102200151670de0b6b3a7640000810281670de0b6b3a764000082041861534c5790500461046052670de0b6b3a764000161046051101561376a5761046051670de0b6b3a7640000036104605261377d565b670de0b6b3a76400006104605103610460525b610460516fffffffffffffffffffffffffffffffff811161534c576002810a90506104405101610440526001018181186136f0575050610440518060b57101000000000000000000000000000000000082106137e0578160801c91508060401b90505b690100000000000000000082106137fe578160401c91508060201b90505b650100000000008210613818578160201c91508060101b90505b63010000008210613830578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050610440526101e05160056104405104808281188284110218905090506104805261048051610440511115613ca5576040366104a0375f6002905b806104e052610440516104e0516001811161534c5760051b6102c0015161048051610440510380820281158383830414171561534c5790509050610480516104e0516001811161534c5760051b610220015180820281158383830414171561534c579050905080820182811061534c5790509050046104e0516001811161534c5760051b6104a001526001018181186138d1575050610120516103a052610140516103c052610160516103e0525f6002905b806104e0526104e0516001811161534c5760051b6102c001516104e0516001810181811061534c5790506002811161534c5760051b61012001516104e0516001811161534c5760051b6104a0015180820281158383830414171561534c5790509050046104e0516001810181811061534c5790506002811161534c5760051b6103a00152600101818118613983575050602061537d5f395f51637b12e0096105005260e0516105205261010051610540526103a051610560526103c051610580526103e0516105a0525f6105c052602061050060c461051c845afa613a6a573d5f5f3e3d5ffd5b60203d1061534c576105009050516104e0525f6003905b8061050052610500516002811161534c5760051b6103a00151670de0b6b3a7640000810281670de0b6b3a764000082041861534c5790506104e051801561534c578082049050905061052052662386f26fc10000610520511015613ae5575f613af6565b68056bc75e2d631000006105205111155b1561534c57600101818118613a815750506104e0516003810490506103a0525f6002905b80610500526104e051670de0b6b3a7640000810281670de0b6b3a764000082041861534c579050610500516001811161534c5760051b6104a001516003810281600382041861534c579050801561534c5780820490509050610500516001810181811061534c5790506002811161534c5760051b6103a00152600101818118613b1a57505061030051602061537d5f395f5163bad1dc26610500526103a051610520526103c051610540526103e051610560526020610500606461051c845afa613be6573d5f5f3e3d5ffd5b60203d1061534c57610500905051670de0b6b3a7640000810281670de0b6b3a764000082041861534c5790500461034052670de0b6b3a7640001610340511015613c30575f613c5e565b61040051610340518060011b818160011c1861534c579050670de0b6b3a7640000810381811161534c579050115b15613ca5576104a0516040526104c051606052613c7c61050061302b565b610500516102a0526104e051600e55610340516011556102a0516003556102a051815250613cbb565b61038051600e55610420516011556102a0518152505b565b61064051610620511461534c57610660511561534c57613cde610760612d41565b61076080516107205260208101516107405250600b5461076052600c5461078052600d546107a052600154604052613d17610820612e9a565b61082080516107c05260208101516107e052604081015161080052505f61082052610640516002811161534c5760051b610760015161084052610620516002811161534c5760051b610760015161086052610860516106605180820182811061534c5790509050610620516002811161534c5760051b6107600152610620516002811161534c5760051b6107600151610620516002811161534c57600b01556003546108805261088051604052613dcf6108e0612ed6565b6108e080516108a05260208101516108c05250610760516107c05180820281158383830414171561534c579050905061076052600160028101905b806108e052670de0b6b3a76400006108e0516002811161534c5760051b61076001516108e0516001810381811161534c5790506001811161534c5760051b6108a0015180820281158383830414171561534c57905090506108e0516002811161534c5760051b6107c0015180820281158383830414171561534c5790509050046108e0516002811161534c5760051b6107600152600101818118613e0a575050610620516002811161534c5760051b6107c001516108e052600a546109005242610900511115613ff757610860516108e05180820281158383830414171561534c5790509050610860526106205115613f4557670de0b6b3a764000061086051610620516001810381811161534c5790506001811161534c5760051b6108a0015180820281158383830414171561534c579050905004610860525b610620516002811161534c5760051b61076001516109205261086051610620516002811161534c5760051b6107600152602061537d5f395f51637b12e0096109405261072051610960526107405161098052610760516109a052610780516109c0526107a0516109e0525f610a0052602061094060c461095c845afa613fcd573d5f5f3e3d5ffd5b60203d1061534c57610940905051600e5561092051610620516002811161534c5760051b61076001525b600e5461092052610640516002811161534c5760051b6107c0015161094052602061537d5f395f51634a2ab3be6109a052610720516109c052610740516109e05261076051610a005261078051610a20526107a051610a405261092051610a605261064051610a805260406109a060e46109bc845afa614079573d5f5f3e3d5ffd5b60403d1061534c576109a0905080516109605260208101516109805250610640516002811161534c5760051b61076001516109605180820382811161534c579050905061082052610640516002811161534c5760051b6107600180516108205180820382811161534c5790509050815250610820516001810381811161534c57905061082052610640511561415d5761082051670de0b6b3a7640000810281670de0b6b3a764000082041861534c579050610640516001810381811161534c5790506001811161534c5760051b6108a00151801561534c5780820490509050610820525b6108205161094051801561534c5780820490509050610820526402540be40061076051606052610780516080526107a05160a05261419c6109c0612f37565b6109c0516108205180820281158383830414171561534c5790509050046109a052610820516109a05180820382811161534c5790509050610820526106805161082051101561424a5760086109c0527f536c6970706167650000000000000000000000000000000000000000000000006109e0526109c0506109c051806109e001601f825f031636823750506308c379a06109805260206109a052601f19601f6109c051011660440161099cfd5b610840516108205180820382811161534c57905090506108405261084051610640516002811161534c57600b0155610840516109405180820281158383830414171561534c57905090506108405261064051156142e957670de0b6b3a764000061084051610640516001810381811161534c5790506001811161534c5760051b6108a0015180820281158383830414171561534c579050905004610840525b61084051610640516002811161534c5760051b61076001526020610620516002811161534c5760051b60400161535d015f395f5160405261066051606052610820516080526106005160a0526106e05160c0526107005160e0526105e051610100526106c051610120526106a05161014052614363612a10565b6020610640516002811161534c5760051b60400161535d015f395f51604052610820516060526106a0516080526106c05160a05261439f612c4a565b6107205160e0526107405161010052610760516101205261078051610140526107a051610160525f61018052610980516101a0526143de6109c061309d565b6109c051610880526105e0517f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c610620516109c052610660516109e05261064051610a005261082051610a20526109a051610a405261088051610a605260c06109c0a261082051815250565b60603660603760405160038104905060605260035460c052600160028101905b8060e052604051670de0b6b3a7640000810281670de0b6b3a764000082041861534c5790506fffffffffffffffffffffffffffffffff60c051166003810281600382041861534c579050801561534c578082049050905060e0516002811161534c5760051b6060015260c05160801c60c05260010181811861446a575050602061537d5f395f5163bad1dc2660e052606051610100526080516101205260a05161014052602060e0606460fc845afa614525573d5f5f3e3d5ffd5b60203d1061534c5760e0905051815250565b600361024051606052610260516080526102805160a0526145596102c0612f37565b6102c0510260031c6102a0525f6102c0525f6003905b8060051b6101e001516102e0526102c0516102e05180820182811061534c57905090506102c05260010181811861456f57505060036102c051046102e0525f610300525f6003905b8060051b6101e00151610320526102e05161032051116145f55761030051610320516102e0510380820182811061534c579050905061030052614615565b610300516102e051610320510380820182811061534c5790509050610300525b6001018181186145b75750506102a0516103005180820281158383830414171561534c57905090506102c051801561534c5780820490509050620186a0810181811061534c579050815250565b60195460605180820182811061534c579050905060195560176040516020525f5260405f20805460605180820182811061534c57905090508155506040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b60195460805260805160605180820281158383830414171561534c5790509050670de0b6b3a76400008104905060a05260a051156147785760805160a05180820182811061534c579050905060195560176040516020525f5260405f20805460a05180820182811061534c57905090508155506040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60a05160c052602060c0a35b60a051815250565b600b54606052600c54608052600d5460a05260035460c0526001546040526147a9610140612e9a565b610140805160e0526020810151610100526040810151610120525060605160e05180820281158383830414171561534c5790509050606052600160028101905b80610140526fffffffffffffffffffffffffffffffff60c05116610140516002811161534c5760051b60e0015180820281158383830414171561534c579050905061016052610140516002811161534c5760051b606001516101605180820281158383830414171561534c5790509050670de0b6b3a764000081049050610140516002811161534c5760051b6060015260c05160801c60c0526001018181186147e95750506060518152608051602082015260a051604082015250565b6148b16101e0612d41565b6101e080516101a05260208101516101c05250600f546101e0526010546102005260195461022052610200516101e05111156148fb57670de0b6b3a763ffff6102205111156148fe565b60015b1561490857614c07565b5f6003905b8061024052602061535d5f395f516020610240516002811161534c5760051b60400161535d015f395f51186149525747610240516002811161534c57600b01556149b4565b6020610240516002811161534c5760051b60400161535d015f395f516370a082316102605230610280526020610260602461027c845afa614995573d5f5f3e3d5ffd5b60203d1061534c57610260905051610240516002811161534c57600b01555b60010181811861490d575050601154610240526404a817c800610200516101e0510364012a05f20081028164012a05f20082041861534c579050046102605260025463cab4d3db6102a05260206102a060046102bc845afa614a18573d5f5f3e3d5ffd5b60203d1061534c576102a0518060a01c61534c576102e0526102e0905051610280526102805115614a4e57610260511515614a50565b5f5b15614b385761024051670de0b6b3a7640000810281670de0b6b3a764000082041861534c579050610240516102605180820382811161534c5790509050801561534c5780820490509050670de0b6b3a7640000810381811161534c5790506102a052610280516040526102a051606052614acb6102e06146d4565b6102e0516102c0526101e051610260518060011b818160011c1861534c57905080820382811161534c57905090506101e0526101e051600f55610280517f6059a38198b1dc42b3791087d1ff0fbd72b3179553c25f678cd246f52ffaaf596102c0516102e05260206102e0a25b602061537d5f395f51637b12e009610320526101a051610340526101c05161036052614b656102c0614780565b6102c080516103805260208101516103a05260408101516103c052505f6103e052602061032060c461033c845afa614b9f573d5f5f3e3d5ffd5b60203d1061534c576103209050516102a0526102a051600e556102a051604052614bca6102c061444a565b6102c051670de0b6b3a7640000810281670de0b6b3a764000082041861534c579050601954801561534c57808204905090506011556101e0516010555b565b60195460605180820382811161534c579050905060195560176040516020525f5260405f20805460605180820382811161534c57905090508155505f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b6019546102805261028051610220511161534c576002610240511161534c57600b546102a052600c546102c052600d546102e052600154604052614cc0610360612e9a565b6103608051610300526020810151610320526040810151610340525061030051610360526103205161038052610340516103a0525f6103c05261030051670de0b6b3a7640000810281670de0b6b3a764000082041861534c5790506103e05260035461040052610360516102a05180820281158383830414171561534c579050905061036052600160028101905b80610420526fffffffffffffffffffffffffffffffff610400511661044052610420516102405118614dab5761044051610240516002811161534c5760051b610360015180820281158383830414171561534c57905090506103e0525b670de0b6b3a7640000610420516002811161534c5760051b6103600151610420516002811161534c5760051b6102a0015180820281158383830414171561534c57905090506104405180820281158383830414171561534c579050905004610420516002811161534c5760051b61036001526104005160801c61040052600101818118614d4e57505061026051614e4857600e546103c052614eb3565b602061537d5f395f51637b12e009610420526101e0516104405261020051610460526103605161048052610380516104a0526103a0516104c0525f6104e052602061042060c461043c845afa614ea0573d5f5f3e3d5ffd5b60203d1061534c576104209050516103c0525b6103c05161042052610360516104405261038051610460526103a05161048052610240516002811161534c5760051b61036001516003810281600382041861534c5790506102205180820281158383830414171561534c579050905061028051801561534c57808204905090506104a052601454604052614f356104e0612e9a565b6104e0602081019050516104c052610240516002811161534c5760051b61044001516104a0511015614fb457610240516002811161534c5760051b6104400180516104a05180820382811161534c579050905081525061044051606052610460516080526104805160a052614fab6104e0612f37565b6104e0516104c0525b61028051610220516104205180820281158383830414171561534c5790509050046104e0526104c0516104e05180820281158383830414171561534c57905090506404a817c800810490506001810181811061534c57905061050052610500516003810281600382041861534c579050610240516002811161534c5760051b6102a0015180820281158383830414171561534c579050905061042051801561534c578082049050905061052052610420516104e0516105005180820382811161534c579050905080820382811161534c579050905061042052602061537d5f395f51634a2ab3be610560526101e05161058052610200516105a052610360516105c052610380516105e0526103a0516106005261042051610620526102405161064052604061056060e461057c845afa6150f0573d5f5f3e3d5ffd5b60403d1061534c5761056090505161054052610240516002811161534c5760051b61036001516105405180820382811161534c5790509050670de0b6b3a7640000810281670de0b6b3a764000082041861534c5790506103e051801561534c57808204905090506105605261054051610240516002811161534c5760051b6103600152610560518152610420516020820152604081016103605181526103805160208201526103a0516040820152506105205160a082015250565b60805160605160401b60405160801b1717815250565b60805160186040516020525f5260405f20806060516020525f5260405f209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b6060513081146152285780151561522a565b5f5b90501561534c5760176040516020525f5260405f20805460805180820382811161534c579050905081555060176060516020525f5260405f20805460805180820182811061534c57905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b60206154bd5f395f51461461533d577fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472606052602061549d5f395f516080527fd61c1033330c368dfc371f5b1e7133f4794e104642e5a3c87aba7a6a3441c8ff60a0524660c0523060e05260206154dd5f395f516101005260c0604052604080516020820120905081525061534a565b60206154fd5f395f518152505b565b5f80fda165767970657283000309000b000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000cbff3004a20dbfe2731543aa38599a526e0fd6ee000000000000000000000000fc0b1eef20e4c68b3dcf36c4537cfa7ce46ca70b000000000000000000000000e72b141df173b999ae7c1adcbf60cc9833ce56a800000000000000000000000073968b9a57c6e53d41345fd57a6e6ae27d6cdb2f00000000000000000000000000000000000000000000000000000000000000075472695344542b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075472695344542b00000000000000000000000000000000000000000000000000ee1b632eb5b42f48bb1636fd14a7244f9f767f8aea98c52ce48869f0f48c4610000000000000000000000000000000000000000000000000000000000000000106df269b4934b4eb0395775addba5ad301fb7a5e0d16730463e10985414e9e2718b8bf89e7e310ba40bad5f7afbfe1014e18383695cf01f7b0b2f33ffee184b9
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.