Skip to content

Commit

Permalink
Enzyme and Arbitrum integration (#228)
Browse files Browse the repository at this point in the history
- Add: Enzyme vault deployments on Arbitrum
- `WMATIC.symbol()` is now `WPOL` as Polygon token migration is going
  • Loading branch information
miohtama authored Sep 26, 2024
1 parent 43d04e4 commit c700180
Show file tree
Hide file tree
Showing 10 changed files with 457 additions and 10 deletions.
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

0 comments on commit c700180

Please sign in to comment.