|
| 1 | +# @version 0.3.10 |
| 2 | +# pragma evm-version paris |
| 3 | + |
| 4 | +""" |
| 5 | +@title CurveDeposit&StakeZap |
| 6 | +@author Curve.Fi |
| 7 | +@license Copyright (c) Curve.Fi, 2020-2024 - all rights reserved |
| 8 | +@notice A zap to add liquidity to pool and deposit into gauge in one transaction |
| 9 | +""" |
| 10 | + |
| 11 | +version: public(constant(String[8])) = "1.0.0" |
| 12 | + |
| 13 | + |
| 14 | +MAX_COINS: constant(uint256) = 9 |
| 15 | + |
| 16 | +# External Contracts |
| 17 | +from vyper.interfaces import ERC20 |
| 18 | + |
| 19 | +interface Pool2: |
| 20 | + def add_liquidity(amounts: uint256[2], min_mint_amount: uint256): nonpayable |
| 21 | + |
| 22 | +interface Pool3: |
| 23 | + def add_liquidity(amounts: uint256[3], min_mint_amount: uint256): nonpayable |
| 24 | + |
| 25 | +interface PoolStableNg: |
| 26 | + def add_liquidity(_amounts: DynArray[uint256, MAX_COINS], _min_mint_amount: uint256): nonpayable |
| 27 | + |
| 28 | +interface MetaZap: |
| 29 | + def add_liquidity(pool: address, _amounts: DynArray[uint256, MAX_COINS], _min_mint_amount: uint256): nonpayable |
| 30 | + |
| 31 | +interface Gauge: |
| 32 | + def deposit(lp_token_amount: uint256, addr: address): nonpayable |
| 33 | + |
| 34 | +@external |
| 35 | +@nonreentrant('lock') |
| 36 | +def deposit_and_stake( |
| 37 | + deposit: address, |
| 38 | + lp_token: address, |
| 39 | + gauge: address, |
| 40 | + n_coins: uint256, |
| 41 | + coins: DynArray[address, MAX_COINS], |
| 42 | + amounts: DynArray[uint256, MAX_COINS], |
| 43 | + min_mint_amount: uint256, |
| 44 | + use_dynarray: bool, |
| 45 | + pool: address = empty(address), |
| 46 | +) -> uint256: |
| 47 | + """ |
| 48 | + @notice Deposit coins into pool and stake obtained LP tokens into gauge. |
| 49 | + Zap address should be passed to `deposit` arg in case of meta-pool deposit with underlying coins. |
| 50 | + @param deposit Zap address for meta-pool deposit with underlying coins, pool address for other cases. |
| 51 | + @param lp_token The address of LP token |
| 52 | + @param gauge The address of gauge |
| 53 | + @param n_coins The number of tokens (underlying or wrapped for meta-pools) |
| 54 | + @param coins List of addresses of coins (underlying or wrapped for meta-pools) |
| 55 | + @param amounts List of amounts of coins to deposit (underlying or wrapped for meta-pools) |
| 56 | + @param min_mint_amount Minimum amount of LP tokens to mint from the deposit |
| 57 | + @param use_dynarray True - plain stable, meta stable with underlying coins |
| 58 | + False - twocrypto, tricrypto, meta stable with wrapped coins |
| 59 | + @param pool The address of meta-pool in case of deposit with underlying coins |
| 60 | + @return Amount of LP tokens staked into gauge |
| 61 | + """ |
| 62 | + assert n_coins >= 2, 'n_coins must be >=2' |
| 63 | + assert n_coins <= MAX_COINS, 'n_coins must be <=MAX_COINS' |
| 64 | + |
| 65 | + # Ensure allowance for swap or zap |
| 66 | + for i in range(MAX_COINS): |
| 67 | + if i == n_coins: |
| 68 | + break |
| 69 | + if amounts[i] == 0 or ERC20(coins[i]).allowance(self, deposit) > 0: |
| 70 | + continue |
| 71 | + ERC20(coins[i]).approve(deposit, max_value(uint256), default_return_value=True) |
| 72 | + |
| 73 | + # Ensure allowance for gauge |
| 74 | + if ERC20(lp_token).allowance(self, gauge) == 0: |
| 75 | + ERC20(lp_token).approve(gauge, max_value(uint256)) |
| 76 | + |
| 77 | + # Transfer coins from owner |
| 78 | + for i in range(MAX_COINS): |
| 79 | + if i == n_coins: |
| 80 | + break |
| 81 | + |
| 82 | + if amounts[i] > 0: |
| 83 | + assert ERC20(coins[i]).transferFrom(msg.sender, self, amounts[i], default_return_value=True) |
| 84 | + |
| 85 | + # Deposit into pool |
| 86 | + if pool != empty(address): # meta-pool deposit with underlying coins, deposit is zap here |
| 87 | + MetaZap(deposit).add_liquidity(pool, amounts, min_mint_amount) |
| 88 | + elif use_dynarray: # plain stable pool |
| 89 | + PoolStableNg(deposit).add_liquidity(amounts, min_mint_amount) |
| 90 | + else: |
| 91 | + if n_coins == 2: # twocrypto or meta-pool deposit with wrapped coins |
| 92 | + Pool2(deposit).add_liquidity([amounts[0], amounts[1]], min_mint_amount) |
| 93 | + elif n_coins == 3: # tricrypto |
| 94 | + Pool3(deposit).add_liquidity([amounts[0], amounts[1], amounts[2]], min_mint_amount) |
| 95 | + else: |
| 96 | + raise "Wrong arguments" |
| 97 | + |
| 98 | + # Stake into gauge |
| 99 | + lp_token_amount: uint256 = ERC20(lp_token).balanceOf(self) |
| 100 | + assert lp_token_amount > 0 # dev: swap-token mismatch |
| 101 | + |
| 102 | + Gauge(gauge).deposit(lp_token_amount, msg.sender) |
| 103 | + |
| 104 | + return lp_token_amount |
| 105 | + |
| 106 | + |
| 107 | +@payable |
| 108 | +@external |
| 109 | +def __default__(): |
| 110 | + pass |
0 commit comments