add-vault-protocol
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdd vault protocol
添加金库协议
This skill guides you through adding support for a new ERC-4626 vault protocol to the eth_defi library.
本指南将引导你为eth_defi库添加对新的ERC-4626金库协议的支持。
Required inputs
所需输入
Before starting, gather the following information from the user:
- Vault smart contract address - The address of an example vault contract on a blockchain
- Protocol name - Human-readable name (e.g., "Plutus", "IPOR", "Morpho")
- Protocol slug - Snake_case identifier for code (e.g., "plutus", "ipor", "morpho")
- Chain - Which blockchain (Ethereum, Arbitrum, Base, etc.)
- Block explorer URL - To fetch the ABI (e.g., Etherscan, Arbiscan, Basescan)
- Single vault protocol: Some protocols, especially ones issuing out their own stablecoin, are know to have only a single vault for the stablecoin staking. Example protocols are like like Spark, Ethena, Cap. In this case use classification later, as there is no point to create complex vault smart contract detection patterns if the protocol does not need it.
HARDCODED_PROTOCOLS - Risk level: Optional. If not given, set to
None
开始前,请从用户处收集以下信息:
- 金库智能合约地址 - 区块链上的示例金库合约地址
- 协议名称 - 易读的名称(例如:"Plutus"、"IPOR"、"Morpho")
- 协议标识(slug) - 代码中使用的蛇形命名标识符(例如:"plutus"、"ipor"、"morpho")
- 区块链 - 目标区块链(Ethereum、Arbitrum、Base等)
- 区块浏览器URL - 用于获取ABI(例如:Etherscan、Arbiscan、Basescan)
- 单金库协议:部分协议,尤其是发行自有稳定币的协议,通常仅拥有一个用于稳定币质押的金库。例如Spark、Ethena、Cap等协议。这种情况下后续请使用分类,因为若协议不需要复杂的金库智能合约检测模式,就无需创建此类模式。
HARDCODED_PROTOCOLS - 风险等级:可选。若未提供,设为
None
Step-by-step implementation
分步实现
Step 1: Download and store the ABI
步骤1:下载并存储ABI
- Fetch the vault smart contract ABI from the blockchain explorer
- Important: If the contract is a proxy, you need the implementation ABI, not the proxy ABI
- Check if the contract has a function or similar
implementation() - Use the explorer's "Read as Proxy" feature to get the implementation address
- Download the implementation contract's ABI
- Check if the contract has a
- Create the ABI directory and file:
eth_defi/abi/{protocol_slug}/ eth_defi/abi/{protocol_slug}/{ContractName}.json - Use as a reference for structure
eth_defi/abi/lagoon/
- 从区块浏览器获取金库智能合约的ABI
- 重要提示:如果合约是代理合约,你需要的是实现合约的ABI,而非代理合约的ABI
- 检查合约是否有或类似函数
implementation() - 使用浏览器的“Read as Proxy”功能获取实现合约地址
- 下载实现合约的ABI
- 检查合约是否有
- 创建ABI目录和文件:
eth_defi/abi/{protocol_slug}/ eth_defi/abi/{protocol_slug}/{ContractName}.json - 参考的目录结构
eth_defi/abi/lagoon/
Step 2: Create the vault class
步骤2:创建金库类
Create following the patterns in:
eth_defi/erc_4626/vault_protocol/{protocol_slug}/vault.py- - Simple vault with hardcoded fees
eth_defi/erc_4626/vault_protocol/plutus/vault.py - - Complex vault with custom fee reading and multicall support
eth_defi/erc_4626/vault_protocol/ipor/vault.py
The vault class should:
python
"""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 check the protocol website to find a direct link URL pattern to its vault. Usual formats:
get_link()- By address
- By chain id and address - for example Ethereum chain id is 1
- By chain name and address - use or simiar
get_chain_name(chain_id).lower() - Can be special for protocols just with one vault, it can be a single link with no pattern
- If you fail to figure this out, just link to the protocol homepage
参考以下文件的模式,创建:
eth_defi/erc_4626/vault_protocol/{protocol_slug}/vault.py- - 带有硬编码手续费的简单金库
eth_defi/erc_4626/vault_protocol/plutus/vault.py - - 支持自定义手续费读取和多调用的复杂金库
eth_defi/erc_4626/vault_protocol/ipor/vault.py
金库类应遵循以下模板:
python
"""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()- 按地址
- 按链ID和地址 - 例如以太坊链ID为1
- 按链名称和地址 - 使用或类似方法
get_chain_name(chain_id).lower() - 对于单金库协议,可直接使用固定链接,无需动态生成
- 若无法找到合适格式,直接链接到协议主页即可
Step 3: Add protocol feature enum
步骤3:添加协议功能枚举
Edit and add a new enum member to :
eth_defi/erc_4626/core.pyERC4626Featurepython
#: {Protocol Name}
#:
#: {Protocol URL}
{protocol_slug}_like = "{protocol_slug}_like"Also update to return the protocol name:
get_vault_protocol_name()python
elif ERC4626Feature.{protocol_slug}_like in features:
return "{Protocol Name}"编辑,在中添加新的枚举成员:
eth_defi/erc_4626/core.pyERC4626Featurepython
#: {Protocol Name}
#:
#: {Protocol URL}
{protocol_slug}_like = "{protocol_slug}_like"同时更新以返回协议名称:
get_vault_protocol_name()python
elif ERC4626Feature.{protocol_slug}_like in features:
return "{Protocol Name}"Step 4: Add protocol identification probes
步骤4:添加协议识别探测逻辑
Edit :
eth_defi/erc_4626/classification.py- In , add a probe call that uniquely identifies this protocol:
create_probe_calls()- Analyse the ABI and the vault implementation smart contract source code to find a function unique to this protocol
- Look for functions like , custom role constants, etc. and compare them to what is already implemented in
getProtocolSpecificData()create_probe_calls() - Make sure this call does not conflict with already configured protocols
- You can also use blockchain explorer's Contract > Read contract or Contract Read contract as proxy to figure out good ABI calls to detect this particular type of smart contracts
- If the protocol is a single vault protocol, use in classification.py instead
HARDCODED_PROTOCOLS
If you cannot find a such accessor function in the ABI or vault smart contract source, interrupt the skill and ask for user intervention.
python
undefined编辑:
eth_defi/erc_4626/classification.py- 在中,添加一个能唯一识别该协议的探测调用:
create_probe_calls()- 分析ABI和金库实现智能合约的源代码,找到该协议特有的函数
- 寻找如、自定义角色常量等函数,并与
getProtocolSpecificData()中已实现的内容对比create_probe_calls() - 确保该调用不会与已配置的协议冲突
- 你也可以使用区块浏览器的“Contract > Read contract”或“Contract Read contract as proxy”功能,找出适合检测此类智能合约的ABI调用
- 若为单金库协议,直接使用classification.py中的即可
HARDCODED_PROTOCOLS
如果在ABI或金库智能合约源代码中找不到此类访问函数,请中断流程并请求用户协助。
python
undefined{Protocol Name}
{Protocol Name}
{Block explorer link}
{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
2. In `identify_vault_features()`, add detection logic:
```python
if calls["uniqueFunction"].success:
features.add(ERC4626Feature.{protocol_slug}_like){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
2. 在`identify_vault_features()`中添加检测逻辑:
```python
if calls["uniqueFunction"].success:
features.add(ERC4626Feature.{protocol_slug}_like)Step 5: Update create_vault_instance()
步骤5:更新create_vault_instance()
In , add a case for the new protocol in :
eth_defi/erc_4626/classification.pycreate_vault_instance()python
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/erc_4626/classification.pycreate_vault_instance()python
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)Step 5: Update risk and fee information
步骤5:更新风险与手续费信息
Update with the protocol stub.
eth_defi/vault/risk.pySet the initial risk level for the protocol in .
USe if not given and this will be later updated by human judgement.
VAULT_PROTOCOL_RISK_MATRIXNoneUpdate with the protocol stub.
eth_defi/vault/fee.pySet to for newly added protocol.
VAULT_PROTOCOL_FEE_MATRIXNoneMatch for the protocol name spelling.
get_vault_protocol_name()在中添加协议的占位内容。
eth_defi/vault/risk.py在中设置协议的初始风险等级,若未提供则设为,后续可由人工判断更新。
VAULT_PROTOCOL_RISK_MATRIXNone在中添加协议的占位内容。
eth_defi/vault/fee.py将中新添加的协议设为。
VAULT_PROTOCOL_FEE_MATRIXNone确保协议名称的拼写与中的一致。
get_vault_protocol_name()Step 6: Create test file
步骤6:创建测试文件
First the latest block number for the selected chain using skill.
get-block-numberCreate following the pattern in and. :
tests/erc_4626/vault_protocol/test_{protocol_slug}.pytests/erc_4626/vault_protocol/test_plutus.pytests/erc_4626/vault_protocol/test_goat.pypython
"""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
- Update the test file for a correct blockchain
- Use the blockchain explorer to get the latest block number using given JSON-RPC URL and Python's Web3.py call
web3.eth.block_number - When you run the test and if the user does not have JSON-RPC configured for this chain, interrupt the skill and tell user to update his test environment variables
After adding it, run the test module and fix any issues.
首先使用工具获取所选链的最新区块号。
get-block-number参考和的模式,创建:
tests/erc_4626/vault_protocol/test_plutus.pytests/erc_4626/vault_protocol/test_goat.pytests/erc_4626/vault_protocol/test_{protocol_slug}.pypython
"""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
- 根据目标区块链更新测试文件
- 使用区块浏览器或Python的Web3.py库的调用,获取给定JSON-RPC URL对应的最新区块号
web3.eth.block_number - 若用户未配置该链的JSON-RPC,请中断流程并告知用户更新测试环境变量
添加完成后,运行测试模块并修复所有问题。
Step 7: Add module init.py
步骤7:添加模块__init__.py
Create :
eth_defi/erc_4626/vault_protocol/{protocol_slug}/__init__.pypython
"""{Protocol Name} protocol integration."""创建:
eth_defi/erc_4626/vault_protocol/{protocol_slug}/__init__.pypython
"""{Protocol Name} protocol integration."""Step 8: Update documentation
步骤8:更新文档
- Add protocol to
docs/source/vaults - Include protocol name
- Search web for a short description, two paragraph
- Add a link to the protocol home page and documentation
- Search Github/web for a Github repo link of the smart contracts
- Search protocol homepage for Twitter link and add it to the documentation
- Search protocol homepage and documentation audits page
- Search protocol homepage and documentation fees page
- Check if DefiLLama has a page for this protocol
- Add the new modules to the protocol index page TOC
- Add the protocol to the master index in
docs/source/vaults/index.rst
Examples include
- ,
docs/source/vaults/plutus/index.rst,docs/source/vaults/truefi/index.rst,docs/source/api/vaults/index.rst
- 将协议添加到目录
docs/source/vaults - 包含协议名称
- 在网上搜索协议的简短介绍,撰写两段内容
- 添加协议主页和文档的链接
- 搜索协议智能合约的Github仓库链接并添加
- 搜索协议主页的Twitter链接并添加到文档中
- 搜索协议主页和文档中的审计页面
- 搜索协议主页和文档中的手续费说明页面
- 检查DefiLlama是否有该协议的页面
- 将新模块添加到协议索引页面的目录中
- 将协议添加到的主索引中
docs/source/vaults/index.rst
示例参考:
- 、
docs/source/vaults/plutus/index.rst、docs/source/vaults/truefi/index.rstdocs/source/api/vaults/index.rst
Step 9: Run all vault protocol detection tests
步骤9:运行所有金库协议检测测试
Check that all ERC-4626 tests pass after adding a new vault protocol by running all testse in folder.
tests/erc_4626/vault_protocolRun all vault testes:
source .local-test.env && poetry run pytest -n auto -k vault_protocolFix any issues if found.
添加新金库协议后,运行文件夹中的所有测试,确保所有ERC-4626测试通过。
tests/erc_4626/vault_protocol运行所有金库测试:
source .local-test.env && poetry run pytest -n auto -k vault_protocol若发现问题,请及时修复。
Step 10: Format the codebase
步骤10:格式化代码库
Format the newly added files with .
poetry run ruff format使用格式化新添加的文件。
poetry run ruff formatStep 11: Add metadata and logos
步骤11:添加元数据与Logo
Read and use it to write a YAML file for the vault protocol in .
eth_defi/data/vaults/README.mdeth_defi/data/vaults/metadata- Create the metadata YAML file
- Use skill to save the vault protocol original logo files
extract-vault-protocol-logo - Use skill to create a light variant of the logo
post-process-logo
AFTER COMPLETING THIS STEP REMEMBER TO CONTINUE WITH THE MAIN TASK.
阅读,并按照说明在中为金库协议创建YAML文件。
eth_defi/data/vaults/README.mdeth_defi/data/vaults/metadata- 创建元数据YAML文件
- 使用工具保存金库协议的原始Logo文件
extract-vault-protocol-logo - 使用工具创建Logo的浅色变体
post-process-logo
完成此步骤后,请继续执行主任务。
Step 12: Verification checklist
步骤12:验证清单
After implementation, verify:
- ABI file is correctly placed in
eth_defi/abi/{protocol_slug}/ - Vault class inherits from
ERC4626Vault - enum has the new protocol
ERC4626Feature - returns the correct name
get_vault_protocol_name() - has a unique probe for the protocol
create_probe_calls() - correctly identifies the protocol
identify_vault_features() - creates the correct vault class
create_vault_instance() - Test file runs successfully with:
source .local-test.env && poetry run pytest tests/erc_4626/vault_protocol/test_{protocol_slug}.py -v - API documents have been updated
- Check that homepage link in the API documentation takes to the correct homepage
- Check that Twitter link in the API documentation works and takes to the same Twitter account as listed on the protocol homepage
If there are problems with the checklist, ask for human assistance.
实现完成后,请验证以下内容:
- ABI文件已正确放置在目录中
eth_defi/abi/{protocol_slug}/ - 金库类继承自
ERC4626Vault - 枚举已添加新协议
ERC4626Feature - 返回正确的协议名称
get_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 -v - API文档已更新
- 检查API文档中的主页链接是否指向正确的页面
- 检查API文档中的Twitter链接是否有效,且与协议主页上的账号一致
若清单中有未完成或存在问题的项,请请求人工协助。
Step 13: Changelog
步骤13:更新变更日志
- Update changelog line in and add a note of added new protocol
CHANGELOG.md
在中添加一行变更记录,说明新增了该协议。
CHANGELOG.mdStep 14: Pull request (optional)
步骤14:创建拉取请求(可选)
After everything is done, open a pull request, but only if the user asks you to.
shell
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所有工作完成后,若用户要求,可创建拉取请求:
shell
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 masterFinding unique protocol identifiers
寻找唯一协议标识符
To find a function that uniquely identifies the protocol:
-
Read the ABI and look for:
- Protocol-specific role constants (e.g., for Plutus)
SAY_TRADER_ROLE() - Custom getter functions (e.g., for IPOR)
getPerformanceFeeData() - Protocol registry calls (e.g., for Morpho)
MORPHO() - Unique configuration functions
- Protocol-specific role constants (e.g.,
-
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:python
name = calls["name"].result if name: name = name.decode("utf-8", errors="ignore") if "ProtocolName" in name: features.add(ERC4626Feature.{protocol_slug}_like)
若要找到能唯一识别协议的函数,请按以下步骤操作:
-
阅读ABI并寻找:
- 协议特有的角色常量(例如Plutus的)
SAY_TRADER_ROLE() - 自定义 getter 函数(例如IPOR的)
getPerformanceFeeData() - 协议注册调用(例如Morpho的)
MORPHO() - 唯一的配置函数
- 协议特有的角色常量(例如Plutus的
-
验证该函数是否真正唯一,确保它不存在于其他协议中
-
若找不到唯一函数,部分协议可使用名称检测:python
name = calls["name"].result if name: name = name.decode("utf-8", errors="ignore") if "ProtocolName" in name: features.add(ERC4626Feature.{protocol_slug}_like)
Example ABI structure
示例ABI结构
The ABI JSON file should contain the contract's ABI array. Example:
json
{
"abi": [
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]
}Or just the array directly:
json
[
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]ABI JSON文件应包含合约的ABI数组,示例如下:
json
{
"abi": [
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]
}或直接使用数组格式:
json
[
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]