more-vaults
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdd more vaults to protocol
为协议添加更多金库
This skill guides you through adding more smart contract types to the existing vaults.
本技能将引导你为现有金库添加更多智能合约类型。
Required inputs
必要输入信息
Before starting, gather the following information from the user:
- Vault protocol name: The exiting vault protocol in the codebase
- Vault class name: What is the Python class name we use for this vault smart contract type
- Vault smart contract address - The address of an example vault contract on a blockchain
- 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
开始前,请从用户处收集以下信息:
- 金库协议名称:代码库中已有的金库协议
- 金库类名称:我们为此类金库智能合约使用的Python类名
- 金库智能合约地址 - 区块链上某一示例金库合约的地址
- 区块链 - 具体区块链(如Ethereum、Arbitrum、Base等)
- 区块浏览器URL - 用于获取ABI(如Etherscan、Arbiscan、Basescan)
- 单一金库协议:部分协议,尤其是发行自有稳定币的协议,通常仅设有一个用于稳定币质押的金库。例如Spark、Ethena、Cap等协议。这种情况下,后续请使用分类,因为如果协议不需要复杂的金库智能合约检测模式,就无需创建此类模式。
HARDCODED_PROTOCOLS
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:创建金库类
Update the existing vault protocol Python module to contain a class definitions.
The vault class should be in a new module next to the existing module:
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 {VaultClassName}(ERC4626Vault):
"""Protocol vault support.
Add few lines descriptiong of the protocol here, from the protocol documentation.
- Add links to protocol documentation
- Add links to example contracts on block explorers
- Add links to github
"""
def has_custom_fees(self) -> bool:
"""Whether this vault has deposit/withdrawal fees."""
return False # Adjust based on protocol
def get_management_fee(self, block_identifier: BlockIdentifier) -> float:
"""Get the current management fee as a percent.
:return:
0.1 = 10%
"""
# Implement based on protocol's fee structure
# Generated: Human can add details later
return None
def get_performance_fee(self, block_identifier: BlockIdentifier) -> float | None:
"""Get the current performance fee as a percent.
:return:
0.1 = 10%
"""
# Implement based on protocol's fee structure
# Generated: Human can add details later
return None
def get_estimated_lock_up(self) -> datetime.timedelta | None:
"""Get estimated lock-up period if any."""
return None
def get_link(self, referral: str | None = None) -> str:
"""Get the vault's web UI link."""
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
更新现有的金库协议Python模块,添加类定义。
金库类应放在现有模块旁的新模块中:
python
"""描述该协议的模块文档字符串。"""
import datetime
import logging
from eth_typing import BlockIdentifier
from eth_defi.erc_4626.vault import ERC4626Vault
logger = logging.getLogger(__name__)
class {VaultClassName}(ERC4626Vault):
"""协议金库支持。
在此添加从协议文档中摘录的协议描述。
- 添加协议文档链接
- 添加区块浏览器上的示例合约链接
- 添加GitHub链接
"""
def has_custom_fees(self) -> bool:
"""该金库是否有存/取款手续费。"""
return False # 根据协议调整
def get_management_fee(self, block_identifier: BlockIdentifier) -> float:
"""获取当前管理费比例。
:return:
0.1 = 10%
"""
# 根据协议的手续费结构实现
# 生成提示:后续由人工补充细节
return None
def get_performance_fee(self, block_identifier: BlockIdentifier) -> float | None:
"""获取当前业绩费比例。
:return:
0.1 = 10%
"""
# 根据协议的手续费结构实现
# 生成提示:后续由人工补充细节
return None
def get_estimated_lock_up(self) -> datetime.timedelta | None:
"""获取预估锁仓时长(若有)。"""
return None
def get_link(self, referral: str | None = None) -> str:
"""获取金库的Web UI链接。"""
return f"https://protocol-url.com/vault/{self.vault_address}"对于,请查看协议官网以找到金库的直接链接URL格式。常见格式包括:
get_link()- 按地址
- 按链ID和地址 - 例如Ethereum的链ID为1
- 按链名称和地址 - 使用或类似方法
get_chain_name(chain_id).lower() - 对于仅含单个金库的协议,可能是固定链接,无格式可言
- 若无法确定格式,直接链接到协议首页即可
Step 3: Add protocol identification probes
步骤3:添加协议识别探测
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 test file
步骤5:更新测试文件
Update 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)
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 and update it in .
fork_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.
参考和的格式,更新:
tests/erc_4626/vault_protocol/test_plutus.pytests/erc_4626/vault_protocol/test_goat.pytests/erc_4626/vault_protocol/test_{protocol_slug}.pypython
"""测试{Protocol Name}金库元数据"""
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}才能运行这些测试"
)
@pytest.fixture(scope="module")
def anvil_{chain}_fork(request) -> AnvilLaunch:
"""基于特定区块创建分叉以保证可复现性"""
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)
return web3
@flaky.flaky
def test_{protocol_slug}(
web3: Web3,
tmp_path: Path,
):
"""读取{Protocol Name}金库元数据"""
vault = create_vault_instance_autodetect(
web3,
vault_address="{vault_address}",
)
assert isinstance(vault, {ProtocolName}Vault)
assert vault.get_protocol_name() == "{Protocol Name}"
# 在此添加关于金库功能标志的断言,例如:
# assert vault.features == {ERC4626Feature.goat_like}
# 添加已知手续费数据的断言
# assert vault.get_management_fee("latest") == ...
# assert vault.get_performance_fee("latest") == ...
# 添加协议风险等级的断言
# assert vault.get_risk() == VaultTechnicalRisk.unknown
- 针对正确的区块链更新测试文件
- 使用区块浏览器获取最新区块号,并更新中的值
fork_block_number - 运行测试时,如果用户未配置该区块链的JSON-RPC,请中断技能并告知用户更新测试环境变量
添加完成后,运行测试模块并修复所有问题。
Step 7: All vault protocol related tests
步骤7:所有金库协议相关测试
Rerun all tests for the vault protocl for which we added more vaults.
重新运行我们添加更多金库的金库协议的所有测试。
Step 8: Format the codebase
步骤8:格式化代码库
Format the newly added files with .
poetry run ruff format使用格式化新添加的文件。
poetry run ruff formatStep 9: Update documentation
步骤9:更新文档
In include the newly created module alongside the existing module.
docs/source/vaultsRemember to update .
index.rst在中,将新创建的模块与现有模块一同包含进来。
docs/source/vaults记得更新。
index.rstStep 10: Verification checklist
步骤10:验证检查清单
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链接是否可用,且指向协议首页列出的同一Twitter账号
若检查清单存在问题,请请求人工协助。
Step 12: Changelog
步骤12:更新变更日志
- Update changelog line in and add a note of added new protocol
CHANGELOG.md
- 在中添加变更记录,注明新增了新协议
CHANGELOG.md
Step 12: Pull request (optional)
步骤12:创建拉取请求(可选)
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)
要找到能唯一识别协议的函数,请按以下步骤操作:
1.阅读ABI并寻找:
- 协议特定的角色常量(例如Plutus的)
SAY_TRADER_ROLE() - 自定义 getter 函数(例如IPOR的)
getPerformanceFeeData() - 协议注册调用(例如Morpho的)
MORPHO() - 唯一的配置函数
-
验证该函数确实唯一,不会在其他协议中出现
-
若不存在唯一函数,部分协议可基于名称检测: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"
}
]