关于 more-vaults
用于在现有代码库中添加 ERC-4626 金库协议支持的编码技能,涵盖 ABI 处理、协议识别、金库实现、测试与文档更新。
主要功能
- 01收集协议和合约信息
- 02添加并保存合约 ABI
- 03实现协议金库类
- 04添加协议识别与实例创建
- 05更新测试、文档和变更日志
适用场景
- 01集成新的金库协议
- 02添加协议专属的金库识别
- 03测试并记录金库支持
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tradingstrategy-ai/ web3-ethereum-defi --skill more-vaults安装后在 Claude Code 中输入/more-vaults确认已加载。
详细介绍
向协议添加更多 Vault
本技能将指导你向现有 Vault 添加更多智能合约类型。
必需输入
开始前,请向用户收集以下信息:
- Vault 协议名称:代码库中现有的 Vault 协议
- Vault 类名:此 Vault 智能合约类型使用的 Python 类名
- Vault 智能合约地址:区块链上一个示例 Vault 合约的地址
- 链:所用的区块链(Ethereum、Arbitrum、Base 等)
- 区块浏览器 URL:用于获取 ABI(例如 Etherscan、Arbiscan、Basescan)
- 单一 Vault 协议:某些协议,尤其是发行自有稳定币的协议,已知只有一个用于稳定币质押的 Vault。例如 Spark、Ethena 和 Cap 等协议。在这种情况下,后续应使用
HARDCODED_PROTOCOLS分类,因为如果协议不需要,就没有必要创建复杂的 Vault 智能合约检测模式。
分步实施
步骤 1:下载并保存 ABI
- 从区块链浏览器获取 Vault 智能合约 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/的目录结构
步骤 2:创建 Vault 类
更新现有 Vault 协议 Python 模块,加入类定义。
Vault 类应放在与现有模块相邻的新模块中:
"""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}"
对于 get_link(),请查看协议网站,找到指向其 Vault 的直接链接 URL 模式。常见格式:
- 使用地址
- 使用链 ID 和地址,例如 Ethereum 的链 ID 为 1
- 使用链名称和地址——使用
get_chain_name(chain_id).lower()或类似方法 - 对于只有一个 Vault 的协议,可以使用没有模式的单一链接
- 如果无法确定,就链接到协议主页
步骤 3:添加协议识别探测
编辑 eth_defi/erc_4626/classification.py:
- 在
create_probe_calls()中添加一个能够唯一识别此协议的探测调用:- 分析 ABI 和 Vault 实现智能合约源代码,找到此协议独有的函数
- 查找
getProtocolSpecificData()、自定义角色常量等函数,并与create_probe_calls()中已实现的内容进行比较 - 确保此调用不会与已配置的协议冲突
- 也可以使用区块链浏览器的 Contract > Read contract 或 Contract Read contract as proxy,找出适合检测此类智能合约的 ABI 调用
- 如果该协议是单一 Vault 协议,请在 classification.py 中使用
HARDCODED_PROTOCOLS
如果在 ABI 或 Vault 智能合约源代码中找不到此类访问器函数,请中断此技能并请求用户介入。
# {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()中添加检测逻辑:
if calls["uniqueFunction"].success:
features.add(ERC4626Feature.{protocol_slug}_like)
步骤 5:更新 create_vault_instance()
在 eth_defi/erc_4626/classification.py 中,为新协议在 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)
步骤 5:更新测试文件
按照 tests/erc_4626/vault_protocol/test_plutus.py 和 tests/erc_4626/vault_protocol/test_goat.py 的模式更新 tests/erc_4626/vault_protocol/test_{protocol_slug}.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)
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
- 将测试文件更新为使用正确的区块链
- 使用区块链浏览器获取最新区块号,并更新
fork_block_number - 运行测试时,如果用户没有为此链配置 JSON-RPC,请中断此技能,并告知用户更新其测试环境变量
添加后,运行测试模块并修复所有问题。
第 7 步:所有 Vault 协议相关测试
重新运行所有 Vault 协议测试,因为我们为该协议添加了更多 Vault。
第 8 步:格式化代码库
使用 poetry run ruff format 格式化新添加的文件。
第 9 步:更新文档
在 docs/source/vaults 中添加新创建的模块,并与现有模块并列。
记得更新 index.rst。
第 10 步:验证清单
实现后,请验证:
- ABI 文件已正确放置在
eth_defi/abi/{protocol_slug}/ - Vault 类继承自
ERC4626Vault -
ERC4626Feature枚举中包含新协议 -
get_vault_protocol_name()返回正确的名称 -
create_probe_calls()为该协议提供唯一的探测调用 -
identify_vault_features()能正确识别该协议 -
create_vault_instance()能创建正确的 Vault 类 - 测试文件运行成功:
source .local-test.env && poetry run pytest tests/erc_4626/vault_protocol/test_{protocol_slug}.py -v - API 文档已更新
- 检查 API 文档中的主页链接是否指向正确的主页
- 检查 API 文档中的 Twitter 链接是否有效,并指向与协议主页所列相同的 Twitter 账号
如果清单中有未通过的项目,请寻求人类协助。
第 12 步:变更日志
- 更新
CHANGELOG.md中的变更日志条目,并注明新增协议
第 12 步:拉取请求(可选)
全部完成后,可以创建拉取请求,但仅当用户要求时才创建。
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
查找唯一的协议标识符
要查找能够唯一标识该协议的函数:
-
阅读 ABI 并查找:
- 协议专属的角色常量(例如 Plutus 的
SAY_TRADER_ROLE()) - 自定义 getter 函数(例如 IPOR 的
getPerformanceFeeData()) - 协议注册表调用(例如 Morpho 的
MORPHO()) - 唯一的配置函数
- 协议专属的角色常量(例如 Plutus 的
-
检查该函数是否确实唯一,确认其他协议中不存在该函数
-
如果没有唯一函数,有些协议可能需要根据名称进行检测:
name = calls["name"].result if name: name = name.decode("utf-8", errors="ignore") if "ProtocolName" in name: features.add(ERC4626Feature.{protocol_slug}_like)
ABI 结构示例
ABI JSON 文件应包含合约的 ABI 数组。示例:
{
"abi": [
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]
}
或者直接使用数组:
[
{
"inputs": [],
"name": "totalAssets",
"outputs": [{ "type": "uint256" }],
"stateMutability": "view",
"type": "function"
}
]