ワンクリックで
add-vault-protocol
// 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.
// 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.
Identify missing vault curators from vault databases and suggest curator feeder YAML entries
Add a new vault curator to feed metadata and curator detection. Use when the user wants to add a verified curator, protocol-managed curator, or curator alias discovered from vault data.
Check all stablecoin YAML files for Twitter activity and domain availability, adding a checks section to each file
Add a note to specific vault
Prepare a new release by updating version, changelog, and building the package
Extract raw price dataframe for a test case
| name | add-vault-protocol |
| description | 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. |
This skill guides you through adding support for a new ERC-4626 vault protocol to the eth_defi library.
Before starting, gather the following information from the user:
HARDCODED_PROTOCOLS classification later, as there is no point to create complex vault smart contract detection patterns if the protocol does not need it.Noneimplementation() function or similareth_defi/abi/{protocol_slug}/
eth_defi/abi/{protocol_slug}/{ContractName}.json
eth_defi/abi/lagoon/ as a reference for structureCreate eth_defi/erc_4626/vault_protocol/{protocol_slug}/vault.py following the patterns in:
eth_defi/erc_4626/vault_protocol/plutus/vault.py - Simple vault with hardcoded feeseth_defi/erc_4626/vault_protocol/ipor/vault.py - Complex vault with custom fee reading and multicall supportThe vault class should:
"""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}"
For get_link() check the protocol website to find a direct link URL pattern to its vault. Usual formats:
get_chain_name(chain_id).lower() or simiarEdit eth_defi/erc_4626/core.py and add a new enum member to ERC4626Feature:
#: {Protocol Name}
#:
#: {Protocol URL}
{protocol_slug}_like = "{protocol_slug}_like"
Also update get_vault_protocol_name() to return the protocol name:
elif ERC4626Feature.{protocol_slug}_like in features:
return "{Protocol Name}"
Edit eth_defi/erc_4626/classification.py:
create_probe_calls(), add a probe call that uniquely identifies this protocol:
getProtocolSpecificData(), custom role constants, etc. and compare them to what is already implemented in create_probe_calls()HARDCODED_PROTOCOLS in classification.py insteadIf you cannot find a such accessor function in the ABI or vault smart contract source, interrupt the skill and ask for user intervention.
# {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}_call
identify_vault_features(), add detection logic:if calls["uniqueFunction"].success:
features.add(ERC4626Feature.{protocol_slug}_like)
In eth_defi/erc_4626/classification.py, add a case for the new protocol in create_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)
Update eth_defi/vault/risk.py with the protocol stub.
Set the initial risk level for the protocol in VAULT_PROTOCOL_RISK_MATRIX.
USe None if not given and this will be later updated by human judgement.
Update eth_defi/vault/fee.py with the protocol stub.
Set VAULT_PROTOCOL_FEE_MATRIX to None for newly added protocol.
Match get_vault_protocol_name() for the protocol name spelling.
First the latest block number for the selected chain using get-block-number skill.
Create tests/erc_4626/vault_protocol/test_{protocol_slug}.py following the pattern in tests/erc_4626/vault_protocol/test_plutus.py and. tests/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_number callAfter adding it, run the test module and fix any issues.
Create eth_defi/erc_4626/vault_protocol/{protocol_slug}/__init__.py:
"""{Protocol Name} protocol integration."""
docs/source/vaultsdocs/source/vaults/index.rstExamples include
docs/source/vaults/plutus/index.rst, docs/source/vaults/truefi/index.rst, docs/source/api/vaults/index.rst,Check that all ERC-4626 tests pass after adding a new vault protocol by running all testse in tests/erc_4626/vault_protocol folder.
Run all vault testes:
source .local-test.env && poetry run pytest -n auto -k vault_protocol
Fix any issues if found.
Format the newly added files with poetry run ruff format.
Read eth_defi/data/vaults/README.md and use it to write a YAML file for the vault protocol in eth_defi/data/vaults/metadata.
extract-vault-protocol-logo skill to save the vault protocol original logo filespost-process-logo skill to create a light variant of the logoAFTER COMPLETING THIS STEP REMEMBER TO CONTINUE WITH THE MAIN TASK.
After implementation, verify:
eth_defi/abi/{protocol_slug}/ERC4626VaultERC4626Feature enum has the new protocolget_vault_protocol_name() returns the correct namecreate_probe_calls() has a unique probe for the protocolidentify_vault_features() correctly identifies the protocolcreate_vault_instance() creates the correct vault classsource .local-test.env && poetry run pytest tests/erc_4626/vault_protocol/test_{protocol_slug}.py -vIf there are problems with the checklist, ask for human assistance.
CHANGELOG.md and add a note of added new protocolAfter everything is done, open a pull request, but only if the user asks you to.
gh 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 master
To find a function that uniquely identifies the protocol:
Read the ABI and look for:
SAY_TRADER_ROLE() for Plutus)getPerformanceFeeData() for IPOR)MORPHO() for Morpho)Verify the function is truly unique by checking it doesn't exist in other protocols
Some protocols may need name-based detection if no unique function exists:
name = calls["name"].result
if name:
name = name.decode("utf-8", errors="ignore")
if "ProtocolName" in name:
features.add(ERC4626Feature.{protocol_slug}_like)
The ABI JSON file should contain the contract's ABI array. Example:
{
"abi": [
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]
}
Or just the array directly:
[
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]