Spend less on fees, more on crypto. Buy crypto easily with MoonPay Balance. 20M+ users trust MoonPay worldwide.
Don’t invest unless you’re prepared to lose all the money you invest.
3000+ Slots, 20+ Cryptos, 75K Raffle, Sports Promos - World's largest Crypto Casino & Sportsbook - Provably Fair!
Play in crypto to make deposits and withdrawals easy! Register and get a free daily shot at a 100 000 $ jackpot.
Monthly Wagering Contest - $500,000+ rewards. Provably Fair, Low House Edge and best VIP Program!
Daily free Spin 50000 Matic ,760% Deposit Bonus, 20%Rakeback, And Get 1000000 Matic free bonus on BC.Game
Deposit BONUS 300% and Cashbacks. without verification!
Holidays are coming soon! Start betting on 1xBit and get a secret gift from Santa!
Overview
POL Balance
POL Value
$135,008.37 (@ $0.68/POL)Token Holdings
Could not find any matches!
- ERC-20 Tokens (>100)5 MEGAMEGA STAKE T... (MEGA)55,920.03540137 stMATICStaked MATIC... (stMATI...)$43,167.21@0.7719198,501.3649575 POLPolygon Toke... (POL)$134,796.32@0.679115,000 WPOL [ POL-MAT.COM ] Visit to claim rewardERC-20: ! (WPOL [...)15,000 WETH [ 3ETH.NET ] Claim RewardERC-20: ! (WETH [...)15,000 WLD [WWW.GET-WLD.ORG] Visit to claim rewardERC-20: ! (WLD [W...)43,767 AAVE [ACCESS AAVE.ASIA]ERC-20: ! AAVE.as... (AAVE [...)9,860 ACCESS [ ETHNA.VERCEL.APP ] AIRDROPERC-20: ! ETHNA (ACCESS...)57.08 pAAVEERC-20: AAVEPool.... (pAAVE)0.01208743 AMBERC-20: Amberium (AMB)0.001 ARZERC-20: Arzona (ARZ)10,000 AVAXVOUCHER.COM SWAP NOWERC-20: AVAX (AVAXVO...)0.01 BBTCERC-20: Binance W... (BBTC)2 XRPERC-20: Binance-P... (XRP)20,000 BOSQUEERC-20: BOSQUE (BOSQUE)1 deBridge Airdrop https://t.ly/ethersERC-20: deBridge ... (deBrid...)1 deBridge Airdrop https://t.ly/ethersERC-20: deBridge ... (deBrid...)100 DPEXDPEX100 SLiElectronic Lira1,500 FEVOFlappy bird evolution$0.00@0.00100 IGTERC-20: Infinity ... (IGT)69 LotteryHost.comERC-20: LotteryHo... (Lotter...)500 LUMERC-20: Luminai (LUM)10,000 MANTA GET pacificdrop.orgERC-20: MANTA (MANTA ...)1 MBNFTERC-20: mbirdNFT.... (MBNFT)10,000 $mPEPE СLАlМ ▷ mpepe.coERC-20: mPEPE ($mPEPE...)10,000 $mPEPE СLАlМ ▷ mpepe.coERC-20: mPEPE ($mPEPE...)15 MUNDERC-20: Mundialis (MUND)0.88335598 OSSOSSChain$0.01@0.01081ERC20 ***552,780 SYTERC-20: SaveYourT... (SYT)1 SRSSirius by Humanity10,000 STEIN web3-chess.com CRYPTO CHESSERC-20: STN (STEIN ...)10,000 web3-chess.com Airdrop $1,000,000 STNERC-20: STN (web3-c...)0.00001631 TCNERC-20: TCN (TCN)10,000 vAIX AIXBOXES.COM Limited BoxERC-20: vAIX (vAIX A...)1 VELOERC-20: VELOCITY (VELO)200 WGCWild Goat Coin$0.04@0.00021,000 WWW.POOFERS.FUNERC-20: WWW.POOFE... (WWW.PO...)100,000,000,000,000,000ERC20 ***1,765 xBlast AirDrop Link https://xblast-app.xyzERC-20: xBlast (xBlast...)1 YUGERC-20: Yugi (YUG)0.168 pZCXZEN Exchange Token$0.03@0.17667,864 TokenERC-20 TOKEN*[Suspicious]724,900 TokenERC-20 TOKEN*[Suspicious]9,543 TokenERC-20 TOKEN*[Suspicious]900,000,000 TokenERC-20 TOKEN*[Suspicious]3,680 TokenERC-20 TOKEN*[Suspicious]900,000,000 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]32 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]60,000 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]2 TokenERC-20 TOKEN*[Suspicious]1 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Suspicious]13,000 TokenERC-20 TOKEN*[Suspicious]10,000 TokenERC-20 TOKEN*[Unsafe]10,000 TokenERC-20 TOKEN*[Unsafe]10,000 TokenERC-20 TOKEN*[Unsafe]10,000 TokenERC-20 TOKEN*[Unsafe]10,000 TokenERC-20 TOKEN*[Unsafe]1,250 TokenERC-20 TOKEN*[Spam]1,250 TokenERC-20 TOKEN*[Spam]1,250 TokenERC-20 TOKEN*[Spam]1,250 TokenERC-20 TOKEN*[Spam]1,250 TokenERC-20 TOKEN*[Spam]3 TokenERC-20 TOKEN*[Spam]90,000,000 TokenERC-20 TOKEN*[Spam]550,000 TokenERC-20 TOKEN*[Spam]8,726 TokenERC-20 TOKEN*[Spam]8,400 TokenERC-20 TOKEN*[Spam]22,400 TokenERC-20 TOKEN*[Spam]8,273 TokenERC-20 TOKEN*[Spam]8,273 TokenERC-20 TOKEN*[Spam]900,000,000 TokenERC-20 TOKEN*[Spam]175,900 TokenERC-20 TOKEN*[Spam]9,000,000,000 TokenERC-20 TOKEN*[Spam]10,000 TokenERC-20 TOKEN*[Spam]1 TokenERC-20 TOKEN*[Spam]1 TokenERC-20 TOKEN*[Spam]1 TokenERC-20 TOKEN*[Spam]1 TokenERC-20 TOKEN*[Spam]NFT Tokens (26)GIVEAWAY$100,000,000ERC-1155Vouchers5 stETH EventERC-1155NFTVoucher5000$ CYBERERC-1155NFTVoucher5000$ CYBERERC-1155NFTVoucher5000$ CYBERERC-1155Vouchers50M $SHIB VoucherERC-1155AAVE-V3-POSAave Positions AirdropERC-1155APE COINAPE NFT TICKETSERC-1155deBridge Airdrop https://t.ly/ethersdeBridge AirdropERC-721deBridge Airdrop https://t.ly/ethersdeBridge AirdropERC-721UNI-V3-PoSUniswap V3 Positions NFT-V1ERC-1155UNI-V3-PoSUniswap V3 Positions NFT-V1ERC-1155ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]
- Transactions
- Internal Transactions
- Token Transfers (ERC-20)
- NFT Transfers
- Contract
- Events
- Multichain Portfolio
Advanced Filter- Filter by Tx Type:
- Tx
- Internal Tx
- ERC-20
- NFTs
Latest 25 from a total of 65,639 transactions
Transaction Hash MethodBlockFromToRemove_liquidity... 65113711 2024-12-05 12:49:38 22 hrs ago 1733402978 IN 0 POL$0.00 0.01880903 62.14391019 Claim_admin_fees 65068461 2024-12-04 9:58:22 2 days ago 1733306302 IN 0 POL$0.00 0.00351156 29.95731994 Remove_liquidity... 64951972 2024-12-01 11:44:56 4 days ago 1733053496 IN 0 POL$0.00 0.01303267 43.23571213 Remove_liquidity... 64941550 2024-12-01 5:30:42 5 days ago 1733031042 IN 0 POL$0.00 0.00885345 29.25146803 Remove_liquidity... 64917955 2024-11-30 15:24:30 5 days ago 1732980270 IN 0 POL$0.00 0.02483591 82.55823155 Remove_liquidity... 64904115 2024-11-30 7:06:03 6 days ago 1732950363 IN 0 POL$0.00 0.0130272 40 Remove_liquidity... 64867904 2024-11-29 9:21:18 7 days ago 1732872078 IN 0 POL$0.00 0.0122546 40.81615029 Remove_liquidity... 64848389 2024-11-28 21:39:26 7 days ago 1732829966 IN 0 POL$0.00 0.00908125 30.00332627 Claim_admin_fees 64789963 2024-11-27 10:52:31 9 days ago 1732704751 IN 0 POL$0.00 0.01145821 97.75884656 Remove_liquidity... 64739884 2024-11-26 3:58:41 10 days ago 1732593521 IN 0 POL$0.00 0.01109817 36.82124059 Remove_liquidity... 64678526 2024-11-24 14:50:40 11 days ago 1732459840 IN 0 POL$0.00 0.02580332 85.43213296 Remove_liquidity... 64678288 2024-11-24 14:42:16 11 days ago 1732459336 IN 0 POL$0.00 0.02610524 86.23759169 Remove_liquidity... 64668430 2024-11-24 8:52:00 12 days ago 1732438320 IN 0 POL$0.00 0.02818974 93.13106263 Remove_liquidity 64668026 2024-11-24 8:37:39 12 days ago 1732437459 IN 0 POL$0.00 0.00376845 55.14834152 Remove_liquidity... 64654296 2024-11-24 0:14:11 12 days ago 1732407251 IN 0 POL$0.00 0.04861714 160.64402882 Remove_liquidity... 64631630 2024-11-23 10:34:51 13 days ago 1732358091 IN 0 POL$0.00 0.03537821 117.61727378 Remove_liquidity... 64615025 2024-11-23 0:44:08 13 days ago 1732322648 IN 0 POL$0.00 0.17281459 530.56182882 Remove_liquidity... 64595634 2024-11-22 13:02:53 13 days ago 1732280573 IN 0 POL$0.00 0.02975935 98.31986474 Remove_liquidity... 64563410 2024-11-21 17:42:52 14 days ago 1732210972 IN 0 POL$0.00 0.01311148 43.31811132 Claim_admin_fees 64510138 2024-11-20 9:50:15 16 days ago 1732096215 IN 0 POL$0.00 0.01958688 167.09651715 Remove_liquidity... 64432315 2024-11-18 11:20:48 17 days ago 1731928848 IN 0 POL$0.00 0.01283512 41.59307537 Remove_liquidity... 64363225 2024-11-16 17:45:30 19 days ago 1731779130 IN 0 POL$0.00 0.01162096 38.3974847 Remove_liquidity... 64151411 2024-11-11 10:43:29 25 days ago 1731321809 IN 0 POL$0.00 0.01385454 45.7718479 Remove_liquidity... 64127070 2024-11-10 20:08:04 25 days ago 1731269284 IN 0 POL$0.00 0.01935979 62.85810144 Remove_liquidity... 64081630 2024-11-09 16:57:34 26 days ago 1731171454 IN 0 POL$0.00 0.01675247 55.34550899 Latest 25 internal transactions (View All)
Parent Transaction Hash Block From To 65113711 2024-12-05 12:49:38 22 hrs ago 1733402978 1.02215424 POL$0.70 65111379 2024-12-05 11:26:22 23 hrs ago 1733397982 5.08770693 POL$3.46 65093990 2024-12-05 1:09:46 33 hrs ago 1733360986 9.78397081 POL$6.66 65082717 2024-12-04 18:24:14 40 hrs ago 1733336654 11.16044689 POL$7.60 65081692 2024-12-04 17:47:49 41 hrs ago 1733334469 6.0791567 POL$4.14 65071137 2024-12-04 11:33:21 47 hrs ago 1733312001 7.35147505 POL$5.00 65070907 2024-12-04 11:25:11 47 hrs ago 1733311511 2.68197298 POL$1.83 65068837 2024-12-04 10:11:42 2 days ago 1733307102 2.91645449 POL$1.99 65043816 2024-12-03 19:15:20 2 days ago 1733253320 9.57749652 POL$6.52 65042095 2024-12-03 18:12:20 2 days ago 1733249540 21.09697187 POL$14.36 65038832 2024-12-03 16:14:58 2 days ago 1733242498 14.20405184 POL$9.67 65036801 2024-12-03 15:02:56 2 days ago 1733238176 25.12444539 POL$17.10 65034821 2024-12-03 13:52:48 2 days ago 1733233968 22.867097 POL$15.56 65016412 2024-12-03 2:50:14 3 days ago 1733194214 15.88550165 POL$10.81 65007380 2024-12-02 21:15:08 3 days ago 1733174108 18.65067734 POL$12.69 65006214 2024-12-02 20:32:19 3 days ago 1733171539 8.58653815 POL$5.84 65005517 2024-12-02 20:06:30 3 days ago 1733169990 10.35182327 POL$7.05 65004959 2024-12-02 19:45:45 3 days ago 1733168745 9.23343542 POL$6.28 65003534 2024-12-02 18:51:49 3 days ago 1733165509 16.9258757 POL$11.52 65003371 2024-12-02 18:46:01 3 days ago 1733165161 65.32518817 POL$44.46 65002967 2024-12-02 18:31:08 3 days ago 1733164268 96.16283129 POL$65.45 65002943 2024-12-02 18:30:15 3 days ago 1733164215 359.08912514 POL$244.42 65001518 2024-12-02 17:37:57 3 days ago 1733161077 420.24629198 POL$286.04 65001516 2024-12-02 17:37:53 3 days ago 1733161073 1,426.1649597 POL$970.72 64999543 2024-12-02 16:25:44 3 days ago 1733156744 54.21768658 POL$36.90 Loading...LoadingMinimal Proxy Contract for 0x5bca7ddf1bcccb2ee8e46c56bfc9d3cdc77262bc
Contract Name:Vyper_contract
Compiler Versionvyper:0.3.1
Contract Source Code (Vyper language format)
# @version 0.3.1 # (c) Curve.Fi, 2021 # Pool for two crypto assets # Universal implementation which can use both ETH and ERC20s from vyper.interfaces import ERC20 interface Factory: def admin() -> address: view def fee_receiver() -> address: view interface CurveToken: def totalSupply() -> uint256: view def mint(_to: address, _value: uint256) -> bool: nonpayable def mint_relative(_to: address, frac: uint256) -> uint256: nonpayable def burnFrom(_to: address, _value: uint256) -> bool: nonpayable interface WETH: def deposit(): payable def withdraw(_amount: uint256): nonpayable # Events event TokenExchange: buyer: indexed(address) sold_id: uint256 tokens_sold: uint256 bought_id: uint256 tokens_bought: uint256 event AddLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] fee: uint256 token_supply: 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 event CommitNewParameters: deadline: indexed(uint256) admin_fee: uint256 mid_fee: uint256 out_fee: uint256 fee_gamma: uint256 allowed_extra_profit: uint256 adjustment_step: uint256 ma_half_time: uint256 event NewParameters: admin_fee: uint256 mid_fee: uint256 out_fee: uint256 fee_gamma: uint256 allowed_extra_profit: uint256 adjustment_step: uint256 ma_half_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 ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400 MIN_RAMP_TIME: constant(uint256) = 86400 MAX_ADMIN_FEE: constant(uint256) = 10 * 10 ** 9 MIN_FEE: constant(uint256) = 5 * 10 ** 5 # 0.5 bps MAX_FEE: constant(uint256) = 10 * 10 ** 9 MAX_A_CHANGE: constant(uint256) = 10 NOISE_FEE: constant(uint256) = 10**5 # 0.1 bps MIN_GAMMA: constant(uint256) = 10**10 MAX_GAMMA: constant(uint256) = 2 * 10**16 MIN_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER / 10 MAX_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER * 100000 EXP_PRECISION: constant(uint256) = 10**10 N_COINS: constant(int128) = 2 PRECISION: constant(uint256) = 10 ** 18 # The precision to convert to A_MULTIPLIER: constant(uint256) = 10000 # Implementation can be changed by changing this constant WETH20: immutable(address) token: public(address) coins: public(address[N_COINS]) price_scale: public(uint256) # Internal price scale _price_oracle: uint256 # Price target given by MA last_prices: public(uint256) last_prices_timestamp: public(uint256) initial_A_gamma: public(uint256) future_A_gamma: public(uint256) initial_A_gamma_time: public(uint256) future_A_gamma_time: public(uint256) allowed_extra_profit: public(uint256) # 2 * 10**12 - recommended value future_allowed_extra_profit: public(uint256) fee_gamma: public(uint256) future_fee_gamma: public(uint256) adjustment_step: public(uint256) future_adjustment_step: public(uint256) ma_half_time: public(uint256) future_ma_half_time: public(uint256) mid_fee: public(uint256) out_fee: public(uint256) admin_fee: public(uint256) future_mid_fee: public(uint256) future_out_fee: public(uint256) future_admin_fee: public(uint256) balances: public(uint256[N_COINS]) D: public(uint256) factory: public(address) 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 also used internally not_adjusted: bool admin_actions_deadline: public(uint256) # This must be changed for different N_COINS # For example: # N_COINS = 3 -> 1 (10**18 -> 10**18) # N_COINS = 4 -> 10**8 (10**18 -> 10**10) # PRICE_PRECISION_MUL: constant(uint256) = 1 PRECISIONS: uint256 # packed @external def __init__(_weth: address): WETH20 = _weth self.mid_fee = 22022022 @payable @external def __default__(): pass # Internal Functions @internal @view def _get_precisions() -> uint256[2]: p0: uint256 = self.PRECISIONS p1: uint256 = 10 ** shift(p0, -8) p0 = 10 ** bitwise_and(p0, 255) return [p0, p1] @internal @view def xp() -> uint256[N_COINS]: precisions: uint256[2] = self._get_precisions() return [self.balances[0] * precisions[0], self.balances[1] * precisions[1] * self.price_scale / PRECISION] @view @internal def _A_gamma() -> uint256[2]: t1: uint256 = self.future_A_gamma_time A_gamma_1: uint256 = self.future_A_gamma gamma1: uint256 = bitwise_and(A_gamma_1, 2**128-1) A1: uint256 = shift(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 # Less readable but more compact way of writing and converting to uint256 # gamma0: uint256 = bitwise_and(A_gamma_0, 2**128-1) # A0: uint256 = shift(A_gamma_0, -128) # A1 = A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0) # gamma1 = gamma0 + (gamma1 - gamma0) * (block.timestamp - t0) / (t1 - t0) t1 -= t0 t0 = block.timestamp - t0 t2: uint256 = t1 - t0 A1 = (shift(A_gamma_0, -128) * t2 + A1 * t0) / t1 gamma1 = (bitwise_and(A_gamma_0, 2**128-1) * t2 + gamma1 * t0) / t1 return [A1, gamma1] @internal @view def _fee(xp: uint256[N_COINS]) -> uint256: """ f = fee_gamma / (fee_gamma + (1 - K)) where K = prod(x) / (sum(x) / N)**N (all normalized to 1e18) """ fee_gamma: uint256 = self.fee_gamma f: uint256 = xp[0] + xp[1] # sum f = fee_gamma * 10**18 / ( fee_gamma + 10**18 - (10**18 * N_COINS**N_COINS) * xp[0] / f * xp[1] / f ) return (self.mid_fee * f + self.out_fee * (10**18 - f)) / 10**18 ### Math functions @internal @pure def geometric_mean(unsorted_x: uint256[N_COINS], sort: bool) -> uint256: """ (x[0] * x[1] * ...) ** (1/N) """ x: uint256[N_COINS] = unsorted_x if sort and x[0] < x[1]: x = [unsorted_x[1], unsorted_x[0]] D: uint256 = x[0] diff: uint256 = 0 for i in range(255): D_prev: uint256 = D # tmp: uint256 = 10**18 # for _x in x: # tmp = tmp * _x / D # D = D * ((N_COINS - 1) * 10**18 + tmp) / (N_COINS * 10**18) # line below makes it for 2 coins D = (D + x[0] * x[1] / D) / N_COINS if D > D_prev: diff = D - D_prev else: diff = D_prev - D if diff <= 1 or diff * 10**18 < D: return D raise "Did not converge" @internal @view def newton_D(ANN: uint256, gamma: uint256, x_unsorted: uint256[N_COINS]) -> uint256: """ Finding the invariant using Newton method. ANN is higher by the factor A_MULTIPLIER ANN is already A * N**N Currently uses 60k gas """ # Safety checks assert ANN > MIN_A - 1 and ANN < MAX_A + 1 # dev: unsafe values A assert gamma > MIN_GAMMA - 1 and gamma < MAX_GAMMA + 1 # dev: unsafe values gamma # Initial value of invariant D is that for constant-product invariant x: uint256[N_COINS] = x_unsorted if x[0] < x[1]: x = [x_unsorted[1], x_unsorted[0]] assert x[0] > 10**9 - 1 and x[0] < 10**15 * 10**18 + 1 # dev: unsafe values x[0] assert x[1] * 10**18 / x[0] > 10**14-1 # dev: unsafe values x[i] (input) D: uint256 = N_COINS * self.geometric_mean(x, False) S: uint256 = x[0] + x[1] for i in range(255): D_prev: uint256 = D # K0: uint256 = 10**18 # for _x in x: # K0 = K0 * _x * N_COINS / D # collapsed for 2 coins K0: uint256 = (10**18 * N_COINS**2) * x[0] / D * x[1] / D _g1k0: uint256 = gamma + 10**18 if _g1k0 > K0: _g1k0 = _g1k0 - K0 + 1 else: _g1k0 = K0 - _g1k0 + 1 # D / (A * N**N) * _g1k0**2 / gamma**2 mul1: uint256 = 10**18 * D / gamma * _g1k0 / gamma * _g1k0 * A_MULTIPLIER / ANN # 2*N*K0 / _g1k0 mul2: uint256 = (2 * 10**18) * N_COINS * K0 / _g1k0 neg_fprime: uint256 = (S + S * mul2 / 10**18) + mul1 * N_COINS / K0 - mul2 * D / 10**18 # D -= f / fprime D_plus: uint256 = D * (neg_fprime + S) / neg_fprime D_minus: uint256 = D*D / neg_fprime if 10**18 > K0: D_minus += D * (mul1 / neg_fprime) / 10**18 * (10**18 - K0) / K0 else: D_minus -= D * (mul1 / neg_fprime) / 10**18 * (K0 - 10**18) / K0 if D_plus > D_minus: D = D_plus - D_minus else: D = (D_minus - D_plus) / 2 diff: uint256 = 0 if D > D_prev: diff = D - D_prev else: diff = D_prev - D if diff * 10**14 < max(10**16, D): # Could reduce precision for gas efficiency here # Test that we are safe with the next newton_y for _x in x: frac: uint256 = _x * 10**18 / D assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # dev: unsafe values x[i] return D raise "Did not converge" @internal @pure def newton_y(ANN: uint256, gamma: uint256, x: uint256[N_COINS], D: uint256, i: uint256) -> uint256: """ Calculating x[i] given other balances x[0..N_COINS-1] and invariant D ANN = A * N**N """ # Safety checks assert ANN > MIN_A - 1 and ANN < MAX_A + 1 # dev: unsafe values A assert gamma > MIN_GAMMA - 1 and gamma < MAX_GAMMA + 1 # dev: unsafe values gamma assert D > 10**17 - 1 and D < 10**15 * 10**18 + 1 # dev: unsafe values D x_j: uint256 = x[1 - i] y: uint256 = D**2 / (x_j * N_COINS**2) K0_i: uint256 = (10**18 * N_COINS) * x_j / D # S_i = x_j # frac = x_j * 1e18 / D => frac = K0_i / N_COINS assert (K0_i > 10**16*N_COINS - 1) and (K0_i < 10**20*N_COINS + 1) # dev: unsafe values x[i] # x_sorted: uint256[N_COINS] = x # x_sorted[i] = 0 # x_sorted = self.sort(x_sorted) # From high to low # x[not i] instead of x_sorted since x_soted has only 1 element convergence_limit: uint256 = max(max(x_j / 10**14, D / 10**14), 100) for j in range(255): y_prev: uint256 = y K0: uint256 = K0_i * y * N_COINS / D S: uint256 = x_j + y _g1k0: uint256 = gamma + 10**18 if _g1k0 > K0: _g1k0 = _g1k0 - K0 + 1 else: _g1k0 = K0 - _g1k0 + 1 # D / (A * N**N) * _g1k0**2 / gamma**2 mul1: uint256 = 10**18 * D / gamma * _g1k0 / gamma * _g1k0 * A_MULTIPLIER / ANN # 2*K0 / _g1k0 mul2: uint256 = 10**18 + (2 * 10**18) * K0 / _g1k0 yfprime: uint256 = 10**18 * y + S * mul2 + mul1 _dyfprime: uint256 = D * mul2 if yfprime < _dyfprime: y = y_prev / 2 continue else: yfprime -= _dyfprime fprime: uint256 = yfprime / y # y -= f / f_prime; y = (y * fprime - f) / fprime # y = (yfprime + 10**18 * D - 10**18 * S) // fprime + mul1 // fprime * (10**18 - K0) // K0 y_minus: uint256 = mul1 / fprime y_plus: uint256 = (yfprime + 10**18 * D) / fprime + y_minus * 10**18 / K0 y_minus += 10**18 * S / fprime if y_plus < y_minus: y = y_prev / 2 else: y = y_plus - y_minus diff: uint256 = 0 if y > y_prev: diff = y - y_prev else: diff = y_prev - y if diff < max(convergence_limit, y / 10**14): frac: uint256 = y * 10**18 / D assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # dev: unsafe value for y return y raise "Did not converge" @internal @pure def halfpow(power: uint256) -> uint256: """ 1e18 * 0.5 ** (power/1e18) Inspired by: https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol#L128 """ intpow: uint256 = power / 10**18 otherpow: uint256 = power - intpow * 10**18 if intpow > 59: return 0 result: uint256 = 10**18 / (2**intpow) if otherpow == 0: return result term: uint256 = 10**18 x: uint256 = 5 * 10**17 S: uint256 = 10**18 neg: bool = False for i in range(1, 256): K: uint256 = i * 10**18 c: uint256 = K - 10**18 if otherpow > c: c = otherpow - c neg = not neg else: c -= otherpow term = term * (c * x / 10**18) / K if neg: S -= term else: S += term if term < EXP_PRECISION: return result * S / 10**18 raise "Did not converge" ### end of Math functions @internal @view def get_xcp(D: uint256) -> uint256: x: uint256[N_COINS] = [D / N_COINS, D * PRECISION / (self.price_scale * N_COINS)] return self.geometric_mean(x, True) @internal def _claim_admin_fees(): A_gamma: uint256[2] = self._A_gamma() xcp_profit: uint256 = self.xcp_profit xcp_profit_a: uint256 = self.xcp_profit_a # Gulp here for i in range(N_COINS): coin: address = self.coins[i] if coin == WETH20: self.balances[i] = self.balance else: self.balances[i] = ERC20(coin).balanceOf(self) vprice: uint256 = self.virtual_price if xcp_profit > xcp_profit_a: fees: uint256 = (xcp_profit - xcp_profit_a) * self.admin_fee / (2 * 10**10) if fees > 0: receiver: address = Factory(self.factory).fee_receiver() if receiver != ZERO_ADDRESS: frac: uint256 = vprice * 10**18 / (vprice - fees) - 10**18 claimed: uint256 = CurveToken(self.token).mint_relative(receiver, frac) xcp_profit -= fees*2 self.xcp_profit = xcp_profit log ClaimAdminFee(receiver, claimed) total_supply: uint256 = CurveToken(self.token).totalSupply() # Recalculate D b/c we gulped D: uint256 = self.newton_D(A_gamma[0], A_gamma[1], self.xp()) self.D = D self.virtual_price = 10**18 * self.get_xcp(D) / total_supply if xcp_profit > xcp_profit_a: self.xcp_profit_a = xcp_profit @internal @view def internal_price_oracle() -> uint256: price_oracle: uint256 = self._price_oracle last_prices_timestamp: uint256 = self.last_prices_timestamp if last_prices_timestamp < block.timestamp: ma_half_time: uint256 = self.ma_half_time last_prices: uint256 = self.last_prices alpha: uint256 = self.halfpow((block.timestamp - last_prices_timestamp) * 10**18 / ma_half_time) return (last_prices * (10**18 - alpha) + price_oracle * alpha) / 10**18 else: return price_oracle @internal def tweak_price(A_gamma: uint256[2],_xp: uint256[N_COINS], p_i: uint256, new_D: uint256): price_oracle: uint256 = self._price_oracle last_prices: uint256 = self.last_prices price_scale: uint256 = self.price_scale last_prices_timestamp: uint256 = self.last_prices_timestamp p_new: uint256 = 0 if last_prices_timestamp < block.timestamp: # MA update required ma_half_time: uint256 = self.ma_half_time alpha: uint256 = self.halfpow((block.timestamp - last_prices_timestamp) * 10**18 / ma_half_time) price_oracle = (last_prices * (10**18 - alpha) + price_oracle * alpha) / 10**18 self._price_oracle = price_oracle self.last_prices_timestamp = block.timestamp D_unadjusted: uint256 = new_D # Withdrawal methods know new D already if new_D == 0: # We will need this a few times (35k gas) D_unadjusted = self.newton_D(A_gamma[0], A_gamma[1], _xp) if p_i > 0: last_prices = p_i else: # calculate real prices __xp: uint256[N_COINS] = _xp dx_price: uint256 = __xp[0] / 10**6 __xp[0] += dx_price last_prices = price_scale * dx_price / (_xp[1] - self.newton_y(A_gamma[0], A_gamma[1], __xp, D_unadjusted, 1)) self.last_prices = last_prices total_supply: uint256 = CurveToken(self.token).totalSupply() old_xcp_profit: uint256 = self.xcp_profit old_virtual_price: uint256 = self.virtual_price # Update profit numbers without price adjustment first xp: uint256[N_COINS] = [D_unadjusted / N_COINS, D_unadjusted * PRECISION / (N_COINS * price_scale)] xcp_profit: uint256 = 10**18 virtual_price: uint256 = 10**18 if old_virtual_price > 0: xcp: uint256 = self.geometric_mean(xp, True) virtual_price = 10**18 * xcp / total_supply xcp_profit = old_xcp_profit * virtual_price / old_virtual_price t: uint256 = self.future_A_gamma_time if virtual_price < old_virtual_price and t == 0: raise "Loss" if t == 1: self.future_A_gamma_time = 0 self.xcp_profit = xcp_profit norm: uint256 = price_oracle * 10**18 / price_scale if norm > 10**18: norm -= 10**18 else: norm = 10**18 - norm adjustment_step: uint256 = max(self.adjustment_step, norm / 5) needs_adjustment: bool = self.not_adjusted # if not needs_adjustment and (virtual_price-10**18 > (xcp_profit-10**18)/2 + self.allowed_extra_profit): # (re-arrange for gas efficiency) if not needs_adjustment and (virtual_price * 2 - 10**18 > xcp_profit + 2*self.allowed_extra_profit) and (norm > adjustment_step) and (old_virtual_price > 0): needs_adjustment = True self.not_adjusted = True if needs_adjustment: if norm > adjustment_step and old_virtual_price > 0: p_new = (price_scale * (norm - adjustment_step) + adjustment_step * price_oracle) / norm # Calculate balances*prices xp = [_xp[0], _xp[1] * p_new / price_scale] # Calculate "extended constant product" invariant xCP and virtual price D: uint256 = self.newton_D(A_gamma[0], A_gamma[1], xp) xp = [D / N_COINS, D * PRECISION / (N_COINS * p_new)] # We reuse old_virtual_price here but it's not old anymore old_virtual_price = 10**18 * self.geometric_mean(xp, True) / total_supply # Proceed if we've got enough profit # if (old_virtual_price > 10**18) and (2 * (old_virtual_price - 10**18) > xcp_profit - 10**18): if (old_virtual_price > 10**18) and (2 * old_virtual_price - 10**18 > xcp_profit): self.price_scale = p_new self.D = D self.virtual_price = old_virtual_price return else: self.not_adjusted = False # Can instead do another flag variable if we want to save bytespace self.D = D_unadjusted self.virtual_price = virtual_price self._claim_admin_fees() return # If we are here, the price_scale adjustment did not happen # Still need to update the profit counter and D self.D = D_unadjusted self.virtual_price = virtual_price # norm appeared < adjustment_step after if needs_adjustment: self.not_adjusted = False self._claim_admin_fees() @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 i < N_COINS # dev: coin index out of range assert j < N_COINS # 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 p: uint256 = 0 dy: uint256 = 0 in_coin: address = self.coins[i] out_coin: address = self.coins[j] y: uint256 = xp[j] x0: uint256 = xp[i] xp[i] = x0 + dx self.balances[i] = xp[i] price_scale: uint256 = self.price_scale precisions: uint256[2] = self._get_precisions() xp = [xp[0] * precisions[0], xp[1] * price_scale * precisions[1] / PRECISION] prec_i: uint256 = precisions[0] prec_j: uint256 = precisions[1] if i == 1: prec_i = precisions[1] prec_j = precisions[0] # In case ramp is happening t: uint256 = self.future_A_gamma_time if t > 0: x0 *= prec_i if i > 0: x0 = x0 * price_scale / PRECISION x1: uint256 = xp[i] # Back up old value in xp xp[i] = x0 self.D = self.newton_D(A_gamma[0], A_gamma[1], xp) xp[i] = x1 # And restore if block.timestamp >= t: self.future_A_gamma_time = 1 dy = xp[j] - self.newton_y(A_gamma[0], A_gamma[1], xp, self.D, j) # Not defining new "y" here to have less variables / make subsequent calls cheaper xp[j] -= dy dy -= 1 if j > 0: dy = dy * PRECISION / price_scale dy /= prec_j dy -= self._fee(xp) * dy / 10**10 assert dy >= min_dy, "Slippage" y -= dy self.balances[j] = y # Do transfers in and out together # XXX coin vs ETH if use_eth and in_coin == WETH20: assert mvalue == dx # dev: incorrect eth amount else: assert mvalue == 0 # dev: nonzero eth amount if callback_sig == EMPTY_BYTES32: response: Bytes[32] = raw_call( in_coin, _abi_encode( sender, self, dx, method_id=method_id("transferFrom(address,address,uint256)") ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) # dev: failed transfer else: b: uint256 = ERC20(in_coin).balanceOf(self) raw_call( callbacker, concat(slice(callback_sig, 0, 4), _abi_encode(sender, receiver, in_coin, dx, dy)) ) assert ERC20(in_coin).balanceOf(self) - b == dx # dev: callback didn't give us coins if in_coin == WETH20: WETH(WETH20).withdraw(dx) if use_eth and out_coin == WETH20: raw_call(receiver, b"", value=dy) else: if out_coin == WETH20: WETH(WETH20).deposit(value=dy) response: Bytes[32] = raw_call( out_coin, _abi_encode(receiver, dy, method_id=method_id("transfer(address,uint256)")), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) y *= prec_j if j > 0: y = y * price_scale / PRECISION xp[j] = y # Calculate price if dx > 10**5 and dy > 10**5: _dx: uint256 = dx * prec_i _dy: uint256 = dy * prec_j if i == 0: p = _dx * 10**18 / _dy else: # j == 0 p = _dy * 10**18 / _dx self.tweak_price(A_gamma, xp, p, 0) log TokenExchange(sender, i, dx, j, dy) return dy @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 = self._fee(xp) * N_COINS / (4 * (N_COINS-1)) S: uint256 = 0 for _x in amounts: S += _x avg: uint256 = S / N_COINS Sdiff: uint256 = 0 for _x in amounts: if _x > avg: Sdiff += _x - avg else: Sdiff += 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, calc_price: bool) -> (uint256, uint256, uint256, uint256[N_COINS]): token_supply: uint256 = CurveToken(self.token).totalSupply() assert token_amount <= token_supply # dev: token amount more than supply assert i < N_COINS # dev: coin out of range xx: uint256[N_COINS] = self.balances D0: uint256 = 0 precisions: uint256[2] = self._get_precisions() price_scale_i: uint256 = self.price_scale * precisions[1] xp: uint256[N_COINS] = [xx[0] * precisions[0], xx[1] * price_scale_i / PRECISION] if i == 0: price_scale_i = PRECISION * precisions[0] if update_D: D0 = self.newton_D(A_gamma[0], A_gamma[1], xp) else: D0 = self.D D: uint256 = D0 # Charge the fee on D, not on y, e.g. reducing invariant LESS than charging the user fee: uint256 = self._fee(xp) dD: uint256 = token_amount * D / token_supply D -= (dD - (fee * dD / (2 * 10**10) + 1)) y: uint256 = self.newton_y(A_gamma[0], A_gamma[1], xp, D, i) dy: uint256 = (xp[i] - y) * PRECISION / price_scale_i xp[i] = y # Price calc p: uint256 = 0 if calc_price and dy > 10**5 and token_amount > 10**5: # p_i = dD / D0 * sum'(p_k * x_k) / (dy - dD / D0 * y0) S: uint256 = 0 precision: uint256 = precisions[0] if i == 1: S = xx[0] * precisions[0] precision = precisions[1] else: S = xx[1] * precisions[1] S = S * dD / D0 p = S * PRECISION / (dy * precision - dD * xx[i] * precision / D0) if i == 0: p = (10**18)**2 / p return dy, p, D, xp @internal @pure def sqrt_int(x: uint256) -> uint256: """ Originating from: https://github.com/vyperlang/vyper/issues/1266 """ if x == 0: return 0 z: uint256 = (x + 10**18) / 2 y: uint256 = x for i in range(256): if z == y: return y y = z z = (x * 10**18 / z + z) / 2 raise "Did not converge" # External 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: """ Exchange using WETH by default """ return self._exchange(msg.sender, msg.value, i, j, dx, min_dy, use_eth, receiver, ZERO_ADDRESS, EMPTY_BYTES32) @payable @external @nonreentrant('lock') def exchange_underlying(i: uint256, j: uint256, dx: uint256, min_dy: uint256, receiver: address = msg.sender) -> uint256: """ Exchange using ETH """ return self._exchange(msg.sender, msg.value, i, j, dx, min_dy, True, receiver, ZERO_ADDRESS, EMPTY_BYTES32) @payable @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: assert cb != EMPTY_BYTES32 # dev: No callback specified return self._exchange(sender, msg.value, i, j, dx, min_dy, use_eth, receiver, msg.sender, cb) @payable @external @nonreentrant('lock') def add_liquidity(amounts: uint256[N_COINS], min_mint_amount: uint256, use_eth: bool = False, receiver: address = msg.sender) -> uint256: assert amounts[0] > 0 or amounts[1] > 0 # dev: no coins to add 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 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 precisions: uint256[2] = self._get_precisions() price_scale: uint256 = self.price_scale * precisions[1] xp = [xp[0] * precisions[0], xp[1] * price_scale / PRECISION] xp_old = [xp_old[0] * precisions[0], xp_old[1] * price_scale / PRECISION] if not use_eth: assert msg.value == 0 # dev: nonzero eth amount for i in range(N_COINS): coin: address = self.coins[i] if use_eth and coin == WETH20: assert msg.value == amounts[i] # dev: incorrect eth amount if amounts[i] > 0: if (not use_eth) or (coin != WETH20): response: Bytes[32] = raw_call( coin, _abi_encode( msg.sender, self, amounts[i], method_id=method_id("transferFrom(address,address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) # dev: failed transfer if coin == WETH20: WETH(WETH20).withdraw(amounts[i]) amountsp[i] = xp[i] - xp_old[i] t: uint256 = self.future_A_gamma_time if t > 0: old_D = self.newton_D(A_gamma[0], A_gamma[1], xp_old) if block.timestamp >= t: self.future_A_gamma_time = 1 else: old_D = self.D D: uint256 = self.newton_D(A_gamma[0], A_gamma[1], xp) lp_token: address = self.token token_supply: uint256 = CurveToken(lp_token).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 CurveToken(lp_token).mint(receiver, d_token) # Calculate price # p_i * (dx_i - dtoken / token_supply * xx_i) = sum{k!=i}(p_k * (dtoken / token_supply * xx_k - dx_k)) # Simplified for 2 coins p: uint256 = 0 if d_token > 10**5: if amounts[0] == 0 or amounts[1] == 0: S: uint256 = 0 precision: uint256 = 0 ix: uint256 = 0 if amounts[0] == 0: S = xx[0] * precisions[0] precision = precisions[1] ix = 1 else: S = xx[1] * precisions[1] precision = precisions[0] S = S * d_token / token_supply p = S * PRECISION / (amounts[ix] * precision - d_token * xx[ix] * precision / token_supply) if ix == 0: p = (10**18)**2 / p self.tweak_price(A_gamma, xp, p, D) else: self.D = D self.virtual_price = 10**18 self.xcp_profit = 10**18 CurveToken(lp_token).mint(receiver, d_token) assert d_token >= min_mint_amount, "Slippage" log AddLiquidity(receiver, amounts, d_token_fee, token_supply) return d_token @external @nonreentrant('lock') def remove_liquidity(_amount: uint256, min_amounts: uint256[N_COINS], use_eth: bool = False, receiver: address = msg.sender): """ This withdrawal method is very safe, does no complex math """ lp_token: address = self.token total_supply: uint256 = CurveToken(lp_token).totalSupply() CurveToken(lp_token).burnFrom(msg.sender, _amount) balances: uint256[N_COINS] = self.balances amount: uint256 = _amount - 1 # Make rounding errors favoring other LPs a tiny bit for i in range(N_COINS): d_balance: uint256 = balances[i] * amount / total_supply assert d_balance >= min_amounts[i] self.balances[i] = balances[i] - d_balance balances[i] = d_balance # now it's the amounts going out coin: address = self.coins[i] if use_eth and coin == WETH20: raw_call(receiver, b"", value=d_balance) else: if coin == WETH20: WETH(WETH20).deposit(value=d_balance) response: Bytes[32] = raw_call( coin, _abi_encode(receiver, d_balance, method_id=method_id("transfer(address,uint256)")), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) D: uint256 = self.D self.D = D - D * amount / total_supply log RemoveLiquidity(msg.sender, balances, total_supply - _amount) @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: A_gamma: uint256[2] = self._A_gamma() dy: uint256 = 0 D: uint256 = 0 p: uint256 = 0 xp: uint256[N_COINS] = empty(uint256[N_COINS]) future_A_gamma_time: uint256 = self.future_A_gamma_time dy, p, D, xp = self._calc_withdraw_one_coin(A_gamma, token_amount, i, (future_A_gamma_time > 0), True) assert dy >= min_amount, "Slippage" if block.timestamp >= future_A_gamma_time: self.future_A_gamma_time = 1 self.balances[i] -= dy CurveToken(self.token).burnFrom(msg.sender, token_amount) coin: address = self.coins[i] if use_eth and coin == WETH20: raw_call(receiver, b"", value=dy) else: if coin == WETH20: WETH(WETH20).deposit(value=dy) response: Bytes[32] = raw_call( coin, _abi_encode(receiver, dy, method_id=method_id("transfer(address,uint256)")), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) self.tweak_price(A_gamma, xp, p, D) log RemoveLiquidityOne(msg.sender, token_amount, i, dy) return dy @external @nonreentrant('lock') def claim_admin_fees(): self._claim_admin_fees() # Admin parameters @external def ramp_A_gamma(future_A: uint256, future_gamma: uint256, future_time: uint256): assert msg.sender == Factory(self.factory).admin() # dev: only owner assert block.timestamp > self.initial_A_gamma_time + (MIN_RAMP_TIME-1) assert future_time > block.timestamp + (MIN_RAMP_TIME-1) # dev: insufficient time A_gamma: uint256[2] = self._A_gamma() initial_A_gamma: uint256 = shift(A_gamma[0], 128) initial_A_gamma = bitwise_or(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 = shift(future_A, 128) future_A_gamma = bitwise_or(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(): assert msg.sender == Factory(self.factory).admin() # dev: only owner A_gamma: uint256[2] = self._A_gamma() current_A_gamma: uint256 = shift(A_gamma[0], 128) current_A_gamma = bitwise_or(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_admin_fee: uint256, _new_fee_gamma: uint256, _new_allowed_extra_profit: uint256, _new_adjustment_step: uint256, _new_ma_half_time: uint256, ): assert msg.sender == Factory(self.factory).admin() # dev: only owner assert self.admin_actions_deadline == 0 # dev: active action new_mid_fee: uint256 = _new_mid_fee new_out_fee: uint256 = _new_out_fee new_admin_fee: uint256 = _new_admin_fee new_fee_gamma: uint256 = _new_fee_gamma new_allowed_extra_profit: uint256 = _new_allowed_extra_profit new_adjustment_step: uint256 = _new_adjustment_step new_ma_half_time: uint256 = _new_ma_half_time # Fees if new_out_fee < MAX_FEE+1: assert new_out_fee > MIN_FEE-1 # dev: fee is out of range else: new_out_fee = self.out_fee if new_mid_fee > MAX_FEE: new_mid_fee = self.mid_fee assert new_mid_fee <= new_out_fee # dev: mid-fee is too high if new_admin_fee > MAX_ADMIN_FEE: new_admin_fee = self.admin_fee # AMM parameters if new_fee_gamma < 10**18: assert new_fee_gamma > 0 # dev: fee_gamma out of range [1 .. 10**18] else: new_fee_gamma = self.fee_gamma if new_allowed_extra_profit > 10**18: new_allowed_extra_profit = self.allowed_extra_profit if new_adjustment_step > 10**18: new_adjustment_step = self.adjustment_step # MA if new_ma_half_time < 7*86400: assert new_ma_half_time > 0 # dev: MA time should be longer than 1 second else: new_ma_half_time = self.ma_half_time _deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY self.admin_actions_deadline = _deadline self.future_admin_fee = new_admin_fee self.future_mid_fee = new_mid_fee self.future_out_fee = new_out_fee self.future_fee_gamma = new_fee_gamma self.future_allowed_extra_profit = new_allowed_extra_profit self.future_adjustment_step = new_adjustment_step self.future_ma_half_time = new_ma_half_time log CommitNewParameters(_deadline, new_admin_fee, new_mid_fee, new_out_fee, new_fee_gamma, new_allowed_extra_profit, new_adjustment_step, new_ma_half_time) @external @nonreentrant('lock') def apply_new_parameters(): assert msg.sender == Factory(self.factory).admin() # dev: only owner assert block.timestamp >= self.admin_actions_deadline # dev: insufficient time assert self.admin_actions_deadline != 0 # dev: no active action self.admin_actions_deadline = 0 admin_fee: uint256 = self.future_admin_fee if self.admin_fee != admin_fee: self._claim_admin_fees() self.admin_fee = admin_fee mid_fee: uint256 = self.future_mid_fee self.mid_fee = mid_fee out_fee: uint256 = self.future_out_fee self.out_fee = out_fee fee_gamma: uint256 = self.future_fee_gamma self.fee_gamma = fee_gamma allowed_extra_profit: uint256 = self.future_allowed_extra_profit self.allowed_extra_profit = allowed_extra_profit adjustment_step: uint256 = self.future_adjustment_step self.adjustment_step = adjustment_step ma_half_time: uint256 = self.future_ma_half_time self.ma_half_time = ma_half_time log NewParameters(admin_fee, mid_fee, out_fee, fee_gamma, allowed_extra_profit, adjustment_step, ma_half_time) @external def revert_new_parameters(): assert msg.sender == Factory(self.factory).admin() # dev: only owner self.admin_actions_deadline = 0 # View Methods @external @view def get_dy(i: uint256, j: uint256, dx: uint256) -> uint256: assert i != j # dev: same input and output coin assert i < N_COINS # dev: coin index out of range assert j < N_COINS # dev: coin index out of range precisions: uint256[2] = self._get_precisions() price_scale: uint256 = self.price_scale * precisions[1] xp: uint256[N_COINS] = self.balances A_gamma: uint256[2] = self._A_gamma() D: uint256 = self.D if self.future_A_gamma_time > 0: D = self.newton_D(A_gamma[0], A_gamma[1], self.xp()) xp[i] += dx xp = [xp[0] * precisions[0], xp[1] * price_scale / PRECISION] y: uint256 = self.newton_y(A_gamma[0], A_gamma[1], xp, D, j) dy: uint256 = xp[j] - y - 1 xp[j] = y if j > 0: dy = dy * PRECISION / price_scale else: dy /= precisions[0] dy -= self._fee(xp) * dy / 10**10 return dy @view @external def calc_token_amount(amounts: uint256[N_COINS]) -> uint256: token_supply: uint256 = CurveToken(self.token).totalSupply() precisions: uint256[2] = self._get_precisions() price_scale: uint256 = self.price_scale * precisions[1] A_gamma: uint256[2] = self._A_gamma() xp: uint256[N_COINS] = self.xp() amountsp: uint256[N_COINS] = [ amounts[0] * precisions[0], amounts[1] * price_scale / PRECISION] D0: uint256 = self.D if self.future_A_gamma_time > 0: D0 = self.newton_D(A_gamma[0], A_gamma[1], xp) xp[0] += amountsp[0] xp[1] += amountsp[1] D: uint256 = self.newton_D(A_gamma[0], A_gamma[1], xp) d_token: uint256 = token_supply * D / D0 - token_supply d_token -= self._calc_token_fee(amountsp, xp) * d_token / 10**10 + 1 return d_token @view @external def calc_withdraw_one_coin(token_amount: uint256, i: uint256) -> uint256: return self._calc_withdraw_one_coin(self._A_gamma(), token_amount, i, True, False)[0] @external @view def lp_price() -> uint256: """ Approximate LP token price """ return 2 * self.virtual_price * self.sqrt_int(self.internal_price_oracle()) / 10**18 @view @external def A() -> uint256: return self._A_gamma()[0] @view @external def gamma() -> uint256: return self._A_gamma()[1] @external @view def fee() -> uint256: return self._fee(self.xp()) @external @view def get_virtual_price() -> uint256: return 10**18 * self.get_xcp(self.D) / CurveToken(self.token).totalSupply() @external @view def price_oracle() -> uint256: return self.internal_price_oracle() # Initializer @external def initialize( A: uint256, gamma: uint256, mid_fee: uint256, out_fee: uint256, allowed_extra_profit: uint256, fee_gamma: uint256, adjustment_step: uint256, admin_fee: uint256, ma_half_time: uint256, initial_price: uint256, _token: address, _coins: address[N_COINS], _precisions: uint256, ): assert self.mid_fee == 0 # dev: check that we call it from factory self.factory = msg.sender # Pack A and gamma: # shifted A + gamma A_gamma: uint256 = shift(A, 128) A_gamma = bitwise_or(A_gamma, gamma) self.initial_A_gamma = A_gamma self.future_A_gamma = A_gamma self.mid_fee = mid_fee self.out_fee = out_fee self.allowed_extra_profit = allowed_extra_profit self.fee_gamma = fee_gamma self.adjustment_step = adjustment_step self.admin_fee = admin_fee self.price_scale = initial_price self._price_oracle = initial_price self.last_prices = initial_price self.last_prices_timestamp = block.timestamp self.ma_half_time = ma_half_time self.xcp_profit_a = 10**18 self.token = _token self.coins = _coins self.PRECISIONS = _precisions
Contract ABI
[{"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}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fee","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_index","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"CommitNewParameters","inputs":[{"name":"deadline","type":"uint256","indexed":true},{"name":"admin_fee","type":"uint256","indexed":false},{"name":"mid_fee","type":"uint256","indexed":false},{"name":"out_fee","type":"uint256","indexed":false},{"name":"fee_gamma","type":"uint256","indexed":false},{"name":"allowed_extra_profit","type":"uint256","indexed":false},{"name":"adjustment_step","type":"uint256","indexed":false},{"name":"ma_half_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"NewParameters","inputs":[{"name":"admin_fee","type":"uint256","indexed":false},{"name":"mid_fee","type":"uint256","indexed":false},{"name":"out_fee","type":"uint256","indexed":false},{"name":"fee_gamma","type":"uint256","indexed":false},{"name":"allowed_extra_profit","type":"uint256","indexed":false},{"name":"adjustment_step","type":"uint256","indexed":false},{"name":"ma_half_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":"_weth","type":"address"}],"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":"payable","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[2]"},{"name":"min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"payable","type":"function","name":"add_liquidity","inputs":[{"name":"amounts","type":"uint256[2]"},{"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[2]"},{"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[2]"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"},{"name":"use_eth","type":"bool"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"},{"name":"use_eth","type":"bool"},{"name":"receiver","type":"address"}],"outputs":[]},{"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":"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_admin_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_half_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":"get_dy","inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"amounts","type":"uint256[2]"}],"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":"lp_price","inputs":[],"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":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_oracle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"initialize","inputs":[{"name":"A","type":"uint256"},{"name":"gamma","type":"uint256"},{"name":"mid_fee","type":"uint256"},{"name":"out_fee","type":"uint256"},{"name":"allowed_extra_profit","type":"uint256"},{"name":"fee_gamma","type":"uint256"},{"name":"adjustment_step","type":"uint256"},{"name":"admin_fee","type":"uint256"},{"name":"ma_half_time","type":"uint256"},{"name":"initial_price","type":"uint256"},{"name":"_token","type":"address"},{"name":"_coins","type":"address[2]"},{"name":"_precisions","type":"uint256"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"token","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":"price_scale","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"last_prices","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"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":"future_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_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"allowed_extra_profit","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_allowed_extra_profit","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"fee_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_fee_gamma","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"adjustment_step","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_adjustment_step","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ma_half_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_ma_half_time","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":"admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_mid_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_out_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_admin_fee","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":"factory","inputs":[],"outputs":[{"name":"","type":"address"}]},{"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":"admin_actions_deadline","inputs":[],"outputs":[{"name":"","type":"uint256"}]}]
Loading...LoadingLoading...Loading
Loading...Loading
Loading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingLoading...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.
Address QR Code
My Address - Private Name Tag or Note
My Name Tag:
Private Name Tags (up to 35 characters) can be used for easy identification of addressesPrivate Note:
A private note (up to 500 characters) can be attached to this address.
Please DO NOT store any passwords or private keys here.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Before You Copy
Transaction Private Note
This website uses cookies to improve your experience. By continuing to use this website, you agree to its Terms and Privacy Policy.