Loading...
Loading...
Add support for a new ERC-4626 vault protocol. Use when the user wants to integrate a new vault protocol like IPOR, Plutus, Morpho, etc. Requires vault smart contract address, protocol name, and protocol slug as inputs.
npx skill4agent add tradingstrategy-ai/web3-ethereum-defi add-vault-protocolHARDCODED_PROTOCOLSNoneimplementation()eth_defi/abi/{protocol_slug}/
eth_defi/abi/{protocol_slug}/{ContractName}.jsoneth_defi/abi/lagoon/eth_defi/erc_4626/vault_protocol/{protocol_slug}/vault.pyeth_defi/erc_4626/vault_protocol/plutus/vault.pyeth_defi/erc_4626/vault_protocol/ipor/vault.py"""Module docstring describing the protocol."""
import datetime
import logging
from eth_typing import BlockIdentifier
from eth_defi.erc_4626.vault import ERC4626Vault
logger = logging.getLogger(__name__)
class {ProtocolName}Vault(ERC4626Vault):
"""Protocol vault support.
One line description of the protocol.
- Add links to protocol documentation
- Add links to example contracts on block explorers
- Add links to github
- If fee information is documented or available as Github source code, link into it
"""
def get_management_fee(self, block_identifier: BlockIdentifier) -> float:
return None
def get_performance_fee(self, block_identifier: BlockIdentifier) -> float | None:
return None
def get_estimated_lock_up(self) -> datetime.timedelta | None:
return None
def get_link(self, referral: str | None = None) -> str:
return f"https://protocol-url.com/vault/{self.vault_address}"get_link()get_chain_name(chain_id).lower()eth_defi/erc_4626/core.pyERC4626Feature#: {Protocol Name}
#:
#: {Protocol URL}
{protocol_slug}_like = "{protocol_slug}_like"get_vault_protocol_name()elif ERC4626Feature.{protocol_slug}_like in features:
return "{Protocol Name}"eth_defi/erc_4626/classification.pycreate_probe_calls()getProtocolSpecificData()create_probe_calls()HARDCODED_PROTOCOLS# {Protocol Name}
# {Block explorer link}
{protocol_slug}_call = EncodedCall.from_keccak_signature(
address=address,
signature=Web3.keccak(text="uniqueFunction()")[0:4],
function="uniqueFunction",
data=b"",
extra_data=None,
)
yield {protocol_slug}_callidentify_vault_features()if calls["uniqueFunction"].success:
features.add(ERC4626Feature.{protocol_slug}_like)eth_defi/erc_4626/classification.pycreate_vault_instance()elif ERC4626Feature.{protocol_slug}_like in features:
from eth_defi.erc_4626.vault_protocol.{protocol_slug}.vault import {ProtocolName}Vault
return {ProtocolName}Vault(web3, spec, token_cache=token_cache, features=features)eth_defi/vault/risk.pyVAULT_PROTOCOL_RISK_MATRIXNoneeth_defi/vault/fee.pyVAULT_PROTOCOL_FEE_MATRIXNoneget_vault_protocol_name()get-block-numbertests/erc_4626/vault_protocol/test_{protocol_slug}.pytests/erc_4626/vault_protocol/test_plutus.pytests/erc_4626/vault_protocol/test_goat.py"""Test {Protocol Name} vault metadata"""
import os
from pathlib import Path
import pytest
from web3 import Web3
import flaky
from eth_defi.erc_4626.classification import create_vault_instance_autodetect
from eth_defi.erc_4626.core import get_vault_protocol_name
from eth_defi.erc_4626.vault_protocol.{protocol_slug}.vault import {ProtocolName}Vault
from eth_defi.provider.anvil import fork_network_anvil, AnvilLaunch
from eth_defi.provider.multi_provider import create_multi_provider_web3
from eth_defi.vault.base import VaultTechnicalRisk
from eth_defi.erc_4626.core import ERC4626Feature
JSON_RPC_{CHAIN} = os.environ.get("JSON_RPC_{CHAIN}")
pytestmark = pytest.mark.skipif(
JSON_RPC_{CHAIN} is None,
reason="JSON_RPC_{CHAIN} needed to run these tests"
)
@pytest.fixture(scope="module")
def anvil_{chain}_fork(request) -> AnvilLaunch:
"""Fork at a specific block for reproducibility"""
launch = fork_network_anvil(JSON_RPC_{CHAIN}, fork_block_number={block_number})
try:
yield launch
finally:
launch.close()
@pytest.fixture(scope="module")
def web3(anvil_{chain}_fork):
web3 = create_multi_provider_web3(anvil_{chain}_fork.json_rpc_url, retries=2)
return web3
@flaky.flaky
def test_{protocol_slug}(
web3: Web3,
tmp_path: Path,
):
"""Read {Protocol Name} vault metadata"""
vault = create_vault_instance_autodetect(
web3,
vault_address="{vault_address}",
)
assert isinstance(vault, {ProtocolName}Vault)
assert vault.get_protocol_name() == "{Protocol Name}"
# Add assertation about vault feature flags here, like:
# assert vault.features == {ERC4626Feature.goat_like}
# Add assertions for fee data we know
# assert vault.get_management_fee("latest") == ...
# assert vault.get_performance_fee("latest") == ...
# Add assertion for the protcol risk level
# assert vault.get_risk() == VaultTechnicalRisk.unknown
web3.eth.block_numbereth_defi/erc_4626/vault_protocol/{protocol_slug}/__init__.py"""{Protocol Name} protocol integration."""docs/source/vaultsdocs/source/vaults/index.rstdocs/source/vaults/plutus/index.rstdocs/source/vaults/truefi/index.rstdocs/source/api/vaults/index.rsttests/erc_4626/vault_protocolsource .local-test.env && poetry run pytest -n auto -k vault_protocolpoetry run ruff formateth_defi/data/vaults/README.mdeth_defi/data/vaults/metadataextract-vault-protocol-logopost-process-logoeth_defi/abi/{protocol_slug}/ERC4626VaultERC4626Featureget_vault_protocol_name()create_probe_calls()identify_vault_features()create_vault_instance()source .local-test.env && poetry run pytest tests/erc_4626/vault_protocol/test_{protocol_slug}.py -vCHANGELOG.mdgh pr create \
--title "Add new vault protocol: {protocol name}" \
--body $'Protocol: {protocok name}\nHomepage: {homepage link}\nGithub: {github link}\nDocs: {docs link}\nExample contract: {blockchain explorer link}" \
--base masterSAY_TRADER_ROLE()getPerformanceFeeData()MORPHO()name = calls["name"].result
if name:
name = name.decode("utf-8", errors="ignore")
if "ProtocolName" in name:
features.add(ERC4626Feature.{protocol_slug}_like){
"abi": [
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]
}[
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]