yeick010/arbitrage-vault
GitHub: yeick010/arbitrage-vault
一个基于 ERC-4626 标准的 EVM 跨 DEX 套利收益金库智能合约,采用多重安全机制防护,适用于 DeFi 安全研究和测试网部署。
Stars: 0 | Forks: 0
# ArbitrageVault
一个用于 EVM 网络上跨 DEX 套利的 ERC-4626 收益金库(已准备好部署至 Sepolia 测试网)。
使用 Foundry + OpenZeppelin Contracts **v5.0.2** + Solidity **^0.8.24** 构建。
## 目录
1. [架构](#architecture)
2. [安全态势](#security-posture)
3. [目录结构](#directory-layout)
4. [构建、测试、覆盖率](#build-test-coverage)
5. [静态分析](#static-analysis)
6. [部署 (Sepolia)](#deployment-sepolia)
7. [威胁模型](#threat-model)
8. [ABI 接口](#abi-surface)
9. [许可证](#license)
## 架构
```
┌────────────────────────────────────────────────────────────────┐
│ ArbitrageVault │
│ (ERC-4626, Pausable, AC) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ deposit / mint / withdraw / redeem nonReentrant │ │
│ │ executeRebalance(params) onlyKeeper │ │
│ │ HWM accounting → perf fee 10% │ │
│ │ Timelock 48h on setFeeCollector / setStrategy / setOracle│ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ StrategyExecutor FeeCollector OracleAdapter │
│ · whitelisted · immutable · Chainlink primary │
│ routers treasury · TWAP fallback │
│ · minAmountOut · fee BPS ≤ 10% · ≤ 2% deviation │
│ enforced │
└────────────────────────────────────────────────────────────────┘
AccessManager (role registry) · ADMIN_ROLE · KEEPER_ROLE · PAUSER_ROLE
```
### 合约
| 合约 | 用途 |
| --------------------------------- | ---------------------------------------------------------------------------- |
| `src/ArbitrageVault.sol` | ERC-4626 金库,HWM 利润核算,时间锁设置器,紧急暂停 |
| `src/StrategyExecutor.sol` | 执行 UniV3 风格的 `exactInput` 兑换;路由白名单 + 强制滑点保护 |
| `src/FeeCollector.sol` | 将 10% 的绩效费用转发给 **不可变 (immutable)** 国库 |
| `src/OracleAdapter.sol` | Chainlink 主预言机 + TWAP 备用预言机,最大 2% 偏差,陈旧价格回退 |
| `src/AccessManager.sol` | 独立的 AccessControl 注册表,用于 ADMIN / KEEPER / PAUSER 角色 |
| `src/common/Errors.sol` | 所有自定义错误(节省 Gas,结构化的回退数据) |
| `src/common/Const.sol` | 共享常量(BPS 分母,费用上限,最低存款,时间锁延迟) |
### 份额核算 (ERC-4626)
- OpenZeppelin v5 虚拟份额,带有 `_decimalsOffset = 6` → 可抵抗通胀攻击。
- `totalAssets()` 返回金库的实时 ERC20 余额(策略是同步的——没有挂起的托管)。
- `minDeposit = 1e6` wei 和 `maxAssetsPerTx` 限制了每个用户的操作,以防止粉尘攻击和 DoS。
### 费用模型
- 绩效费用**仅对高于高水位线 (HWM) 的已实现利润收取**。
- 费用上限为 **1_000 BPS (10%)**;在 `FeeCollector` 构造函数和每次 `setPerformanceFee` 时均会强制执行。
- 国库是**不可变的 (immutable)**——无法重新分配。
## 安全态势
| 控制项 | 实现方式 |
| ---------------------------------------- | -------------------------------------------------------------------- |
| 重入攻击 | 在 `deposit/mint/withdraw/redeem/rebalance/syncHWM/rescue` 上使用 `ReentrancyGuard` |
| 紧急停止 | 由专用 `PAUSER_ROLE` 控制的 `Pausable` |
| 访问控制 | OZ `AccessControl` (ADMIN / KEEPER / PAUSER) — 无 EOA 所有者 |
| 通胀攻击 | OZ v5 虚拟份额 + `_decimalsOffset = 6` |
| DoS / 粉尘攻击 | `minDeposit = 1e6` + `maxAssetsPerTx` 上限 |
| 预言机操纵 | Chainlink 主预言机 + TWAP 备用预言机,偏差 ≤ 2%,陈旧度检查 |
| 滑点 | `executeRebalance` 上强制要求 `minAmountOut`,在两侧均重新检查 |
| 特权更改 | `setFeeCollector / setStrategy / setOracle` 上的 48 小时时间锁 |
| ERC20 安全性 | 到处使用 `SafeERC20` — `safeTransfer(From)` / `forceApprove` |
| 任意 `from` | 已消除 — `FeeCollector.collect` & `StrategyExecutor` 使用 `msg.sender` |
| CEI (检查-生效-交互) | 效果(HWM 更新,事件)在外部调用之前发出 |
| 非标准代币 | `forceApprove` 处理 USDT 风格的 `approve` 回退 |
| 多签就绪 | 所有 admin / pauser / treasury 都是为多签设计的纯地址 |
### 预审计清单状态
- [x] 在所有资金流动入口点上使用 `ReentrancyGuard`
- [x] 无 `tx.origin` 授权
- [x] 无对不可信代码的 `delegatecall`
- [x] 所有 ERC20 交互通过 `SafeERC20`
- [x] 源码中没有硬编码地址
- [x] 每次状态更改都会发出事件(在分支处注明原因:`ChangeScheduled/Applied/Cancelled`)
- [x] `.env` 位于 `.gitignore` 中,已提供 `.env.example`
- [x] 预言机陈旧度 + 偏差检查
- [x] 测试覆盖率 ≥ 90% 行(已达到 **97.98%**)
- [x] 在关键路径上进行 10_000 次运行的模糊测试
- [x] 不变式测试(费用上限,minPricePerShare,资产守恒)
- [x] Slither 0 个高危,0 个中危(仅保留有意的时间戳比较低危问题)
## 目录结构
```
arbitrage-vault/
├── src/
│ ├── ArbitrageVault.sol
│ ├── StrategyExecutor.sol
│ ├── FeeCollector.sol
│ ├── OracleAdapter.sol
│ ├── AccessManager.sol
│ ├── common/{Const.sol,Errors.sol}
│ └── interfaces/{IFeeCollector,IOracleAdapter,IStrategyExecutor}.sol
├── test/
│ ├── Base.t.sol # shared test harness
│ ├── ArbitrageVault.t.sol # 39 unit / fuzz tests
│ ├── FeeCollector.t.sol # 15 unit tests
│ ├── OracleAdapter.t.sol # 18 unit tests
│ ├── StrategyExecutor.t.sol # 5 unit tests
│ ├── AccessManager.t.sol # 5 unit tests
│ ├── Invariants.t.sol # 4 invariant tests + handler
│ └── mocks/{MockERC20,MockChainlinkFeed,MockTWAPSource,MockSwapRouter}.sol
├── script/
│ └── Deploy.s.sol # Sepolia deployment entrypoint
├── audit/
│ ├── slither.md # Slither checklist output
│ └── THREAT_MODEL.md # STRIDE-based threat model
├── config/ # (populated per network)
├── deployments/ # (populated post-deploy)
├── foundry.toml
├── remappings.txt
├── .env.example
├── CHANGELOG.md
└── README.md
```
## 构建、测试、覆盖率
### 前置条件
- [Foundry](https://book.getfoundry.sh/) (`forge`, `cast`, `anvil`)
- Python 3.9+ (用于 Slither)
### 命令
```
# 安装依赖项(版本已锁定)
forge install
# 编译
forge build
# 运行完整测试套件并启用 traces
forge test -vvv
# 仅运行 fuzz/invariant 配置(10_000 次 fuzz 运行,64 次 invariant 运行 × 深度 32)
forge test
# 快速 CI 配置(1_000 次 fuzz 运行)
FOUNDRY_PROFILE=ci forge test
# 覆盖率摘要(仅限 src)
FOUNDRY_PROFILE=ci forge coverage --no-match-coverage "test/" --report summary
# gas 报告
forge test --gas-report
```
### 测试结果(最近一次运行)
```
Ran 6 test suites: 86 tests passed, 0 failed, 0 skipped (86 total tests)
src/AccessManager.sol 100.00% (15/15) 100.00% funcs
src/ArbitrageVault.sol 98.44% (126/128) 96.55% funcs
src/FeeCollector.sol 100.00% (27/27) 100.00% funcs
src/OracleAdapter.sol 96.15% (50/52) 100.00% funcs
src/StrategyExecutor.sol 96.15% (25/26) 100.00% funcs
────────────────────────────────────────────────────────
Total: 97.98% lines · 94.24% stmts · 98.00% funcs
```
## 静态分析
### Slither
```
slither . --filter-paths "lib|test" --checklist > audit/slither.md
```
当前结果(参见 `audit/slither.md`):
| 严重程度 | 数量 | 状态 |
| -------- | ----- | ------------ |
| 高危 | 0 | ✔ 已全部修复 |
| 中危 | 0 | ✔ 已全部修复 |
| 低危 | 4 | 符合预期(参见 audit/slither.md——用于 deadline/timelock/staleness 的时间戳 + 良性的授权重置) |
| 信息 | 0 | — |
提交历史记录了每次修复:
- `fix(security): eliminate arbitrary-send-erc20 HIGH` — 移除了 `FeeCollector.collect` 和 `StrategyExecutor.executeArbitrage` 中任意的 `from`,这也解决了 3 个 `reentrancy-balance` 高危问题。
- `fix(security): resolve reentrancy-no-eth and unused-return MEDIUM` — `syncHighWaterMark` 增加了 `nonReentrant`;`_getPrimaryPrice` 捕获了 `startedAt`。
- `fix(security): address Slither informational findings` — 在 `ChangeScheduled/Applied` 中索引了地址,CEI 重新排序了 `rescueToken`。
- `fix(security): move highWaterMark update before fee-collect external call (CEI)` — 将最后一个中危降级为良性的低危。
## 部署 (Sepolia)
### 1. 复制并填写环境变量
```
cp .env.example .env
$EDITOR .env # fill PRIVATE_KEY, RPC, asset, admin multisig, etc.
source .env
```
关键:`.env` 已被 git 忽略。**切勿**提交或将私钥粘贴到日志中。
### 2. 试运行
```
forge script script/Deploy.s.sol \
--rpc-url $SEPOLIA_RPC_URL \
-vvvv
```
在广播之前,请检查打印出的 `POST-DEPLOY CHECKLIST`。
### 3. 广播 + 验证
```
forge script script/Deploy.s.sol \
--rpc-url $SEPOLIA_RPC_URL \
--broadcast \
--verify
```
### 4. 部署后连接(通过 admin 多签)
1. `executor.setRouterWhitelist(, true)`
2. `vault.scheduleStrategy(executor)` → 等待 **48 小时** → `vault.applyStrategy()`
3. (可选) `feeCollector.setAuthorisedCollector(vault, true)` 如果 admin ≠ 部署者
4. 使用打印出的地址更新 `deployments/sepolia.env`
5. 标记发行版:`git tag v0.1.0-sepolia && git push --tags`
### 5. 链上冒烟测试
```
cast call $VAULT "totalSupply()(uint256)" --rpc-url $SEPOLIA_RPC_URL
cast call $VAULT "asset()(address)" --rpc-url $SEPOLIA_RPC_URL
cast call $FEE_COLLECTOR "treasury()(address)" --rpc-url $SEPOLIA_RPC_URL
```
## 威胁模型
有关完整的 STRIDE 演练,请参见 [`audit/THREAT_MODEL.md`](audit/THREAT_MODEL.md)。
总结——该系统可防御:
| 威胁类别 | 示例 | 缓解措施 |
| ------------------ | ----------------------------------------- | -------------------------------------------------------------------------- |
| **S**poofing (欺骗) | EOA 伪装成金库 | 在执行器上使用 `onlyVault` (不可变);到处使用基于 `msg.sender` 的授权 |
| **T**ampering (篡改) | 恶意管理员替换国库 / 策略 | 国库不可变;strategy/fc/oracle 受 48 小时时间锁保护 |
| **R**epudiation (抵赖) | 否认费用转发 | `FeesCollected` 事件 + 明确的链上转账日志 |
| **I**nfo disclosure (信息泄露)| 预言机价格泄露 | 不适用——价格是公开的;TWAP 防止操纵 |
| **D**oS (拒绝服务) | 粉尘存款泛滥,预言机数据陈旧 | `minDeposit`,`maxAssetsPerTx`,`StalePrice` 回退 |
| **E**levation (提权) | Keeper 尝试管理员操作 | 分离的 ADMIN / KEEPER / PAUSER 角色;无跨角色隐式授权 |
| 经济性 | 通胀攻击,夹子攻击,闪电贷 | 虚拟份额,滑点限制,偏差限制,nonReentrant |
## ABI 接口
关键金库入口点:
```
// ERC-4626 standard
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function mint(uint256 shares, address receiver) external returns (uint256 assets);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
// Vault-specific
function executeRebalance(IStrategyExecutor.ArbitrageParams calldata params)
external returns (uint256 profit);
function pause() external; // PAUSER_ROLE
function unpause() external; // ADMIN_ROLE
function syncHighWaterMark() external; // ADMIN_ROLE
// Timelocked (48 h)
function scheduleFeeCollector(address newValue) external;
function applyFeeCollector() external;
function cancelFeeCollector() external;
// … and identical trio for strategy, oracle
```
完整的构建产物会在 `forge build` 之后输出到 `out/`。要导出 ABI:
```
forge inspect ArbitrageVault abi > abi/ArbitrageVault.json
forge inspect FeeCollector abi > abi/FeeCollector.json
forge inspect OracleAdapter abi > abi/OracleAdapter.json
```
## 许可证
MIT — 请参阅每个源文件中的 SPDX 标头。
由 ArbitrageVault 开发团队构建。未经第三方审计;**在没有独立安全审查的情况下请勿用于生产环境**。
标签:AMM, CEI模式, Chainlink, CISA项目, DeFi, ERC-4626, EVM, Foundry, HWM, OpenZeppelin, Sepolia测试网, Solidity, Streamlit, TWAP, Vault, Web3, 代币化策略, 加密货币, 区块链, 套利, 安全, 审计, 性能费, 提示词模板, 提示词注入, 收益, 收益聚合器, 时间锁, 智能合约, 智能合约开发, 暂停机制, 流动性管理, 算法交易, 自动化做市商, 访问控制, 超时处理, 跨DEX, 跨去中心化交易所, 量化交易, 金库, 防重入, 预言机, 高水位线