Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enzyme and Arbitrum integration #228

Merged
merged 9 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Current

- Add: TokenSniffer API wrapper with a persistent cache
- Add: Enzyme vault deployments on Arbitrum

# 0.26

Expand Down
7 changes: 7 additions & 0 deletions eth_defi/aave_v3/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,22 @@ class AaveToken(NamedTuple):
},
),
# Arbitrum Mainnet (XXX TODO - add more tokens)
# https://docs.aave.com/developers/deployed-contracts/v3-mainnet/arbitrum
"arbitrum": AaveNetwork(
name="Arbitrum",
pool_address="0x794a61358D6845594F94dc1DB02A252b5b4814aD",
pool_configurator_address="0x8145eddDf43f50276641b55bd3AD95944510021E",
pool_created_at_block=7742429, # https://arbiscan.io/tx/0xf73ad5eb856faaf2eaf6e8a0823d2964e80ca4ad7cc2031f0606b158d236b5a9
# https://github.com/bgd-labs/aave-address-book/blob/main/src/AaveV3Arbitrum.sol
token_contracts={
# Aave token contracts defined in the Arbitrum network
"AAVE": AaveToken(token_address="0xba5DdD1f9d7F570dc94a51479a000E3BCE967196", deposit_address="0xf329e36C7bF6E5E86ce2150875a84Ce77f477375", variable_borrow_address="0xE80761Ea617F66F96274eA5e8c37f03960ecC679", stable_borrow_address="0xfAeF6A702D15428E588d4C0614AEFb4348D83D48", token_created_at_block=7410775), # https://arbiscan.io/address/0xba5ddd1f9d7f570dc94a51479a000e3bce967196
"WETH": AaveToken(token_address="0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", deposit_address="0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8", variable_borrow_address="0x0c84331e39d6658Cd6e6b9ba04736cC4c4734351", stable_borrow_address="0xD8Ad37849950903571df17049516a5CD4cbE55F6", token_created_at_block=55), # https://arbiscan.io/address/0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
"USDT": AaveToken(token_address="0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", deposit_address="0x6ab707Aca953eDAeFBc4fD23bA73294241490620", variable_borrow_address="0xfb00AC187a8Eb5AFAE4eACE434F493Eb62672df7", stable_borrow_address="0x70eFfc565DB6EEf7B927610155602d31b670e802", token_created_at_block=1),
# USDC native (not bridged)
"USDC": AaveToken(token_address="0xaf88d065e77c8cC2239327C5EDb3A432268e5831", deposit_address="0x724dc807b04555b71ed48a6896b6F41593b8C637", variable_borrow_address="0xf611aEb5013fD2c0511c9CD55c7dc5C1140741A6", stable_borrow_address="0xDC1fad70953Bb3918592b6fCc374fe05F5811B6a", token_created_at_block=1),
# USDC bridged
"USDC.e": AaveToken(token_address="0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", deposit_address="0x625E7708f30cA75bfd92586e17077590C60eb4cD", variable_borrow_address="0xFCCf3cAbbe80101232d343252614b6A3eE81C989", stable_borrow_address="0x307ffe186F84a3bc2613D1eA417A5737D69A7007", token_created_at_block=1),
},
),
# Fantom Mainnet (XXX TODO - add more tokens)
Expand Down
21 changes: 21 additions & 0 deletions eth_defi/enzyme/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,27 @@
"allowed_external_position_types_policy": "0x5A739da3099fd4fC954BD764099Fc000Da76D8e7",
}

#: Enzyme deployment details for Arbitrum
#:
#: See :py:meth:`EnzymeDeployment.fetch_deployment`
#:
#: See https://docs.enzyme.finance/general-info/codebase/contracts/arbitrum
#:
ARBITRUM_DEPLOYMENT = {
"comptroller_lib": "0x3868c0fc34b6ece124c6ab122f6f29e978be6661",
"usdc": "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8", # USDC (bridged)
"usdt": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", # USDT
"weth": "0x82af49447d8a07e3bd95bd0d56f35241523fbab1",
"arb": "0x912ce59144191c1204e64559fe8253a0e49e6548",
"fund_value_calculator": "0xea609eeb38d1ee8e8719597d47cc9276df9f8707",
"deployed_at": 23_0330_758, # When comptroller lib was deployed
"cumulative_slippage_tolerance_policy": "0x487f6a8a93c2be5a296ead2c3fbc3fceed4ac599",
"allowed_adapters_policy": "0x1768b813d17f82a8d70bd8b80a8c8c1562878337",
"only_remove_dust_external_position_policy": "0xe4453105be9e579896a3ed73df9a1e285c8c95c2",
"only_untrack_dust_or_priceless_assets_policy": "0xa482f4ab637cd5ca00084d511b3ca9aa8d8f475e",
"allowed_external_position_types_policy": "0x3c441b696bd451d0ba95ebb73cf1b23c20873e14",
}

#: Enzyme deployment details for Ethereum
#:
#: See :py:meth:`EnzymeDeployment.fetch_deployment`
Expand Down
45 changes: 38 additions & 7 deletions eth_defi/enzyme/generic_adapter_vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from web3 import Web3
from web3.contract import Contract

from eth_defi.aave_v3.constants import AAVE_V3_DEPLOYMENTS
from eth_defi.aave_v3.constants import AAVE_V3_DEPLOYMENTS, AAVE_V3_NETWORKS
from eth_defi.aave_v3.deployment import fetch_deployment as fetch_aave_deployment
from eth_defi.enzyme.deployment import EnzymeDeployment
from eth_defi.enzyme.policy import (
Expand Down Expand Up @@ -245,6 +245,9 @@ def deploy_vault_with_generic_adapter(
deployer.sync_nonce(web3)

if terms_of_service is not None:
assert denomination_asset.address
assert comptroller.address
assert terms_of_service.address
payment_forwarder, tx_hash = deploy_contract_with_forge(
web3,
CONTRACTS_ROOT / "in-house",
Expand Down Expand Up @@ -411,8 +414,13 @@ def deploy_guard(
case 1:
uniswap_v2_router = UNISWAP_V2_DEPLOYMENTS["ethereum"]["router"]
uniswap_v3_router = UNISWAP_V3_DEPLOYMENTS["ethereum"]["router"]
case 42161:
if uniswap_v2:
raise NotImplementedError(f"Uniswap v2 not configured for Arbitrum yet")
uniswap_v2_router = None
uniswap_v3_router = UNISWAP_V3_DEPLOYMENTS["arbitrum"]["router"]
case _:
logger.info("Uniswap not supported for chain %d", web3.eth.chain_id)
logger.error("Uniswap not supported for chain %d", web3.eth.chain_id)
uniswap_v2_router = None
uniswap_v3_router = None

Expand Down Expand Up @@ -463,12 +471,35 @@ def deploy_guard(
tx_hash = guard.functions.whitelistAaveV3(aave_pool_address, note).transact({"from": deployer.address})
assert_transaction_success_with_explanation(web3, tx_hash)

assert web3.eth.chain_id == 1, "TODO: Add support for non-mainnet chains"
ausdc_address = "0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c"
logger.info("Aave whitelisting for pool %s, aUSDC %s", aave_pool_address, ausdc_address)
match web3.eth.chain_id:
case 1:
assert web3.eth.chain_id == 1, "TODO: Add support for non-mainnet chains"
ausdc_address = "0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c"
logger.info("Aave whitelisting for pool %s, aUSDC %s", aave_pool_address, ausdc_address)

note = f"Aave v3 pool whitelisting for USDC"
tx_hash = guard.functions.whitelistToken(ausdc_address, note).transact({"from": deployer.address})

case 42161:
# Arbitrum
aave_tokens = AAVE_V3_NETWORKS["arbitrum"].token_contracts

# TODO: We automatically list all main a tokens as allowed assets
# we should limit here only to what the strategy needs,
# as these tokens may have their liquidity to dry up in the future
for symbol, token in aave_tokens.items():
logger.info(
"Aave whitelisting for pool %s, atoken:%s address: %s",
symbol,
aave_pool_address,
token.token_address,
)
note = f"Whitelisting Aave {symbol}"
tx_hash = guard.functions.whitelistToken(token.token_address, note).transact({"from": deployer.address})
assert_transaction_success_with_explanation(web3, tx_hash)
case _:
raise NotImplementedError(f"TODO: Add support for non-mainnet chains, got {web3.eth.chain_id}")

note = f"Aave v3 pool whitelisting for USDC"
tx_hash = guard.functions.whitelistToken(ausdc_address, note).transact({"from": deployer.address})
assert_transaction_success_with_explanation(web3, tx_hash)

deployer.sync_nonce(web3)
Expand Down
8 changes: 7 additions & 1 deletion eth_defi/foundry/forge.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ def deploy_contract_with_forge(
assert type(contract_name) == str
assert isinstance(deployer, HotWallet), f"Got deployer: {type(deployer)}"

assert deployer.private_key is not None, f"Deployer missing private key: {deployer}"

if constructor_args is None:
constructor_args = []

Expand Down Expand Up @@ -236,7 +238,11 @@ def deploy_contract_with_forge(
for arg in constructor_args:
cmd_line.append(arg)

censored_command = " ".join(cmd_line)
try:
censored_command = " ".join(cmd_line)
except TypeError as e:
# Be helpful with None error
raise TypeError(f"Could not splice command line: {cmd_line}") from e

logger.info(
"Deploying a contract with forge. Working directory %s, forge command: %s",
Expand Down
30 changes: 30 additions & 0 deletions tests/enzyme/test_arbitrum_deployment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Get Enzyme deployment on Arbitrum.

- Use Arbitrum live RPC for testing.

"""
import os

import pytest
from web3 import Web3

from eth_defi.enzyme.deployment import EnzymeDeployment, ARBITRUM_DEPLOYMENT
from eth_defi.provider.multi_provider import create_multi_provider_web3

JSON_RPC_ARBITRUM = os.environ.get("JSON_RPC_ARBITRUM")
pytestmark = pytest.mark.skipif(not JSON_RPC_ARBITRUM, reason="Set JSON_RPC_ARBITRUM to run this test")


@pytest.fixture()
def web3():
web3 = create_multi_provider_web3(JSON_RPC_ARBITRUM)
return web3


def test_fetch_enzyme_on_arbitrum(
web3: Web3,
):
"""Fetch Enzyme deployment."""
deployment = EnzymeDeployment.fetch_deployment(web3, ARBITRUM_DEPLOYMENT)
assert deployment.mln.functions.symbol().call() == "MLN"
assert deployment.weth.functions.symbol().call() == "WETH"
Loading
Loading