Skip to content

Commit

Permalink
Enzyme vault cumulative slippage tolerance test (#187)
Browse files Browse the repository at this point in the history
- A manual test for Enzyme's cumulative slippage tolerance policy
  • Loading branch information
miohtama authored Jan 24, 2024
1 parent a0461c6 commit e9c0bfd
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 4 deletions.
61 changes: 61 additions & 0 deletions eth_defi/enzyme/erc20.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,64 @@ def prepare_approve(
)

return bound_call


def prepare_transfer_sneaky(
enzyme: EnzymeDeployment,
vault: Vault,
generic_adapter: Contract,
token: Contract,
receiver: HexAddress | str,
amount: int
) -> ContractFunction:
"""Prepare an ERC-20 transfer out from the Enzyme vault.
- Tells the Enzyme vault to move away some tokes
- Should be blocked by GuardV0, only useable by governance
:param enzyme:
Enzyme deploymeent
:param vault:
Vault that needs to perform the swap
:param generic_adapter:
GenericAdapter contract we use for swaps
:param token:
ERC-20 token we send
:param receiver:
The receiver of tokens
:param amount:
Token amount, raw
:return:
Transaction object that can be signed and executed
"""

spent = int(amount * 0.05)

# Prepare the swap parameters
spend_asset_amounts = [amount]
spend_assets = [token.address]
incoming_assets = [token.address]
min_incoming_assets_amounts = [amount - spent - 10_000]

# The vault performs a swap on Uniswap v2
encoded_transfer = encode_function_call(token.functions.transfer, [receiver, spent])

bound_call = execute_calls_for_generic_adapter(
comptroller=vault.comptroller,
external_calls=((token, encoded_transfer),),
generic_adapter=generic_adapter,
incoming_assets=incoming_assets,
integration_manager=enzyme.contracts.integration_manager,
min_incoming_asset_amounts=min_incoming_assets_amounts,
spend_asset_amounts=spend_asset_amounts,
spend_assets=spend_assets,
)

return bound_call
119 changes: 119 additions & 0 deletions scripts/enzyme/cumulative-slippage-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""Test cumulative slippage tolerance of Enzyme vault."""
import os

from eth_account import Account
from eth_account.signers.local import LocalAccount
from web3.middleware import construct_sign_and_send_raw_middleware

from eth_defi.abi import get_deployed_contract, encode_function_call
from eth_defi.enzyme.deployment import EnzymeDeployment, POLYGON_DEPLOYMENT
from eth_defi.enzyme.generic_adapter import execute_calls_for_generic_adapter
from eth_defi.enzyme.vault import Vault
from eth_defi.provider.multi_provider import create_multi_provider_web3
from eth_defi.token import fetch_erc20_details
from eth_defi.uniswap_v2.deployment import FOREVER_DEADLINE, fetch_deployment

vault_address = os.environ["VAULT"]
json_rpc_url = os.environ["JSON_RPC_POLYGON"]
private_key = os.environ["PRIVATE_KEY"]
receiver = "0x7612A94AafF7a552C373e3124654C1539a4486A8"

web3 = create_multi_provider_web3(json_rpc_url)
account: LocalAccount = Account.from_key(private_key)
web3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))

deployment = EnzymeDeployment.fetch_deployment(web3, POLYGON_DEPLOYMENT)
vault = Vault.fetch(web3, vault_address)

# print("Deploying")
# generic_adapter = deploy_contract(
# web3,
# f"VaultSpecificGenericAdapter.json",
# account.address,
# deployment.contracts.integration_manager.address,
# vault.address,
# )
# print(f"Generic adapter is {generic_adapter.address}")

generic_adapter = get_deployed_contract(
web3,
f"VaultSpecificGenericAdapter.json",
"0x8C35a027FE7986FA5736813869C0A2A7A991BEDd"
)
vault.generic_adapter = generic_adapter

wmatic = fetch_erc20_details(web3, "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270").contract
# balance = wmatic.contract.functions.balanceOf(vault.address).call()
# balance = 1

# USDC
usdc = fetch_erc20_details(web3, "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174").contract
balance = int(usdc.functions.balanceOf(vault.address).call())

# bound_transfer = prepare_transfer_sneaky(
# deployment,
# vault,
# generic_adapter,
# token.contract,
# receiver,
# balance,
# )

swap_amount = balance
token_in = usdc
token_out = wmatic

uniswap_v2 = fetch_deployment(
web3,
"0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32",
"0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff",
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f",
)

# Prepare the swap parameters
token_in_swap_amount = swap_amount
spend_asset_amounts = [token_in_swap_amount]
spend_assets = [token_in]
path = [token_in.address, token_out.address]
expected_outgoing_amount, expected_incoming_amount = uniswap_v2.router.functions.getAmountsOut(token_in_swap_amount, path).call()
extra_slippage = int(expected_incoming_amount * 0.05)
expected_incoming_amount -= extra_slippage
incoming_assets = [token_out]
min_incoming_assets_amounts = [expected_incoming_amount]

# The vault performs a swap on Uniswap v2
encoded_approve = encode_function_call(token_in.functions.approve, [uniswap_v2.router.address, token_in_swap_amount])

# fmt: off
encoded_swapExactTokensForTokens = encode_function_call(
uniswap_v2.router.functions.swapExactTokensForTokens,
[token_in_swap_amount, 1, path, generic_adapter.address, FOREVER_DEADLINE]
)

transfer_spent = extra_slippage - 5

encoded_transfer = encode_function_call(wmatic.functions.transfer, [receiver, transfer_spent])

bound_call = execute_calls_for_generic_adapter(
comptroller=vault.comptroller,
external_calls=(
(token_in, encoded_approve),
(uniswap_v2.router, encoded_swapExactTokensForTokens),
(wmatic, encoded_transfer),
),
generic_adapter=generic_adapter,
incoming_assets=incoming_assets,
integration_manager=deployment.contracts.integration_manager,
min_incoming_asset_amounts=min_incoming_assets_amounts,
spend_asset_amounts=spend_asset_amounts,
spend_assets=spend_assets,
)


tx_hash = bound_call.transact({"from": account.address})
print("Broadcasting", tx_hash.hex())
receipt = web3.eth.wait_for_transaction_receipt(tx_hash)

print("Receipt", receipt)


5 changes: 2 additions & 3 deletions tests/enzyme/test_guard_enzyme_uniswap_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
- transferWithAuthorization() and receiveWithAuthorization() integration tests for Enzyme protocol
"""
import flaky

import pytest
from eth_account import Account
from eth_account.signers.local import LocalAccount
from eth_typing import HexAddress, ChecksumAddress
from eth_typing import HexAddress
from web3 import Web3
from web3.contract import Contract

Expand All @@ -31,7 +31,6 @@
from eth_defi.middleware import construct_sign_and_send_raw_middleware_anvil
from eth_defi.token import TokenDetails
from eth_defi.trace import assert_transaction_success_with_explanation, TransactionAssertionError
from eth_defi.usdc.deployment import deploy_fiat_token
from eth_defi.usdc.eip_3009 import make_eip_3009_transfer, EIP3009AuthorizationType


Expand Down
2 changes: 1 addition & 1 deletion tests/rpc/test_out_of_gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def web3():

@pytest.fixture(scope="module")
def quickswap(web3) -> UniswapV2Deployment:
"""Fetch live quickswap v3 deployment.
"""Fetch live quickswap deployment.
See https://docs.quickswap.exchange/concepts/protocol-overview/03-smart-contracts for more information
"""
Expand Down

0 comments on commit e9c0bfd

Please sign in to comment.