Curve Add and Remove Liquidity

Add and remove liquidity from Curve pools

# pragma version ^0.4.0

from ethereum.ercs import IERC20

interface IStableSwap:
    def add_liquidity(amounts: uint256[3], min_shares: uint256): nonpayable
    def remove_liquidity(shares: uint256, min_amounts: uint256[3]): nonpayable
    def remove_liquidity_one_coin(shares: uint256, i: int128, min_amount: uint256): nonpayable
    def get_virtual_price() -> uint256: view

DAI: constant(address) = 0x6B175474E89094C44Da98b954EedeAC495271d0F
USDC: constant(address) = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
USDT: constant(address) = 0xdAC17F958D2ee523a2206206994597C13D831ec7

POOL: constant(address) = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7
COINS: constant(address[3]) = [DAI, USDC, USDT]

@internal
def _safe_transfer(coin: address, to: address, amount: uint256):
    res: Bytes[32] = raw_call(
        coin,
        concat(
            method_id("transfer(address,uint256)"),
            convert(to, bytes32),
            convert(amount, bytes32),
        ),
        max_outsize=32,
    )
    if len(res) > 0:
        assert convert(res, bool)

@internal
def _safe_transfer_from(coin: address, _from: address, to: address, amount: uint256):
    res: Bytes[32] = raw_call(
        coin,
        concat(
            method_id("transferFrom(address,address,uint256)"),
            convert(_from, bytes32),
            convert(to, bytes32),
            convert(amount, bytes32),
        ),
        max_outsize=32,
    )
    if len(res) > 0:
        assert convert(res, bool)

@internal
def _safe_approve(coin: address, to: address, amount: uint256):
    res: Bytes[32] = raw_call(
        coin,
        concat(
            method_id("approve(address,uint256)"),
            convert(to, bytes32),
            convert(amount, bytes32),
        ),
        max_outsize=32,
    )
    if len(res) > 0:
        assert convert(res, bool)

@external
def add_liquidity(amounts: uint256[3], min_shares: uint256):
    for i: uint256 in range(3):
        if amounts[i] > 0:
            self._safe_transfer_from(COINS[i], msg.sender, self, amounts[i])
            self._safe_approve(COINS[i], POOL, amounts[i])

    extcall IStableSwap(POOL).add_liquidity(amounts, min_shares)

@external
@view
def calc_value_of_shares(shares: uint256) -> uint256:
    return shares * staticcall IStableSwap(POOL).get_virtual_price() // 10**18

@external
def remove_liquidity(shares: uint256, min_amounts: uint256[3]):
    extcall IStableSwap(POOL).remove_liquidity(shares, min_amounts)

    for coin: address in [DAI, USDC, USDT]:
        bal: uint256 = staticcall IERC20(coin).balanceOf(self)
        self._safe_transfer(coin, msg.sender, bal)

@external
def remove_liquidity_one_coin(shares: uint256, i: int128, min_amount: uint256):
    extcall IStableSwap(POOL).remove_liquidity_one_coin(shares, i, min_amount)

    bal: uint256 = staticcall IERC20(COINS[i]).balanceOf(self)
    self._safe_transfer(COINS[i], msg.sender, bal)

Try on Smart Contract Engineer