什么是金库?
"金库"这个概念在 DeFi 中非常形象,理解它就能串起我们之前讨论的所有内容。让我从多个角度帮你建立起一个完整的认知。
1. 从现实世界理解:金库 = 自动化理财保险箱
想象你去银行存钱,但银行给你的不是一张普通的存折,而是一张可以在整个金融市场自由交易的"基金份额凭证"。
| 现实概念 | 对应 ERC-4626 金库 |
|---|---|
| 你存入美元 | 存入基础资产(USDC、ETH) |
| 银行给你一张存单 | 金库铸造并给你份额代币 |
| 银行把你的钱拿去投资(房贷、国债)赚取利息 | 金库将资产投入借贷、质押、做市等策略赚取收益 |
| 存单价值随着银行经营而增长 | 份额代币的价值随金库总资产增长而上升 |
| 你拿存单去银行取回本金+利息 | 你销毁份额代币,赎回更多的基础资产 |
核心区别 :传统银行的存单不能随便转让,但金库的份额代币是标准的 ERC-20,可以自由交易、用作抵押、存入其他协议,这就是 DeFi 可组合性的魅力。
2. 从代码层面理解:金库 = 一个特殊的 ERC-20 合约
ERC-4626 金库本质上是一个包装器------它把另一个 ERC-20 代币(基础资产)包装起来,并赋予它自动生息的能力。
┌─────────────────────────────────────────┐
│ ERC-4626 金库 │
│ ┌─────────────────────────────────┐ │
│ │ 基础资产(如 USDC) │ │
│ │ totalAssets() = 100,000 USDC │ │
│ └─────────────────────────────────┘ │
│ ↓ 映射关系 │
│ ┌─────────────────────────────────┐ │
│ │ 份额代币(如 usdcVault) │ │
│ │ totalSupply() = 90,000 份额 │ │
│ │ 1 份额 = 1.111 USDC │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
金库的核心职责只有几件事:
- 存款:接收资产,铸造份额
- 取款:销毁份额,返还资产,redeem 赎回。【withdraw 提取,逻辑:指定要取出多少基础资产,然后销毁对应数量的份额。简单理解:我要从金库里取出 100 美元,不管需要烧掉多少份额凭证】
- 投资:将闲置资产投入策略赚取收益,由开发者预先编写在智能合约中(如存入 Aave 借贷、质押 Lido、为 Uniswap 提供流动性),完全自动化,透明度完全公开
- 记账 :维护
totalAssets和totalSupply,确保会计平衡
- totalAssets():代表池子里到底有多少水(基础资产,比如 USDC)。
- totalSupply():代表有多少个水桶(份额代币)在池子里舀水。
这就是为什么用totalAssets / totalSupply计算汇率------它精确地反映了你在这个共同池子中的比例所有权。 - 单个份额的价值可以使用:可以通过一个公开的、标准化的函数 (convertToAssets【totalAssets / totalSupply) 随时计算,其他协议(如借贷协议)可以非常安全地接受这个份额代币作为抵押品。它们知道这个代币背后代表着多少底层资产。如果底层资产是 BTC,需要再根据 BTC 的价格换算为 美元
- TVL【Total Value Locked,总锁仓价值】:通常以美元为单位计价。因此如果底层存储的是 BTC,那么就是 池子里到底有多少BTC,然后再根据 BTC 的价格换算为 美元
- AUM【资产管理规模】:在 4626 中代表 TVL
- NAV【 Net Asset Value(净资产价值)】:指一只基金(如共同基金、ETF)每份额所代表的实际资产净值,计算方式:(基金总资产------基金总负债)÷ 已发行的基金份额总数。开放式基金的申购和赎回价格依据(通常每日更新)。
份额代币的运作原理
当你与 ERC-4626 金库交互时,份额代币在其中扮演着关键角色:
-
存入资产,铸造份额
- 你将基础资产(例如 1000 USDC)存入金库。
- 金库根据当时的"汇率"计算,给你铸造并发放相应数量的份额代币。
-
份额增值,而非数量增加
- 金库利用存入的总资产进行投资或产生收益(例如借贷、质押、交易费)。
- 随着金库总资产的增加,每一份份额代币的价值也会随之增长。
- 你的钱包里始终持有相同数量的份额代币,但它们所代表的基础资产变多了。
-
赎回份额,取回资产
- 当你想要取回资产时,你将持有的份额代币"销毁"(返还给金库)。
- 金库根据当前的"汇率",将对应数量的基础资产发送给你。
简单的数学示例
假设一个 ETH 金库:
-
初始状态:
- 金库是空的。
- 汇率是 1 份额 = 1 ETH。
-
你存入 1 ETH:
- 金库为你铸造 1 份额。
- 此时金库总资产 = 1 ETH,总供应量 = 1 份额。1 份额仍然值 1 ETH。
-
金库通过质押 ETH 获得收益,资产增长 50%:
- 现在金库总资产 = 1.5 ETH。
- 总供应量 = 1 份额。
- 汇率更新 :现在 1 份额 = 1.5 ETH。
-
你决定全部赎回:
- 你销毁 1 份额。
- 金库向你支付 1.5 ETH。
- 你赚取了 0.5 ETH 的收益,而你的份额数量从未改变。
为什么需要份额代币?
- 简化记账:金库不需要追踪每个用户的历史存入记录,只需记录谁拥有多少份额。
- 抽象化收益:用户无需手动领取或复投收益,收益会自动体现在份额代币的价值增长中。
- 可组合性:这个份额代币本身是一个标准的 ERC-20 代币,因此它可以被用于其他 DeFi 协议(例如作为抵押品、放入流动性池、再次存入其他金库),极大地提高了资本效率。
总结:在 ERC-4626 标准中,你持有的份额代币数量不变,但其代表的资产价值会随着金库的盈利而增加。
金库是如何通过借贷、质押、交易费产生收益的?
你提到的这三种方式是 DeFi 金库最常见的收益来源。ERC-4626 标准本身只定义了接口,但金库的具体策略由开发者实现。以下是它们产生收益的底层逻辑:
A. 借贷
这是最经典的"存币生息"模式。
- 运作方式:金库将用户存入的资产(例如 USDC),存入到去中心化借贷协议(如 Aave、Compound)的资金池中。
- 收益来源 :借款人需要向资金池支付利息。这些利息累积在借贷协议中,使得金库存放在那里的资产数量被动增加。
- 如何传递给用户 :由于金库总资产增加了,而份额总数不变,导致每一份份额的价值上升。当你赎回时,就能拿回更多的资产。
B. 质押
通常指将资产锁定在区块链网络中,以维护网络安全并获得奖励。
- 运作方式 :
- PoS 质押:如果金库持有的是 ETH 等 PoS 资产,它可以将其质押到以太坊信标链,参与区块验证。
- 流动性质押:更常见的是,金库将资产通过 Lido 或 Rocket Pool 等协议,换成流动性质押代币(如 stETH),并持有这些生息代币。
- 收益来源:网络协议会增发新的代币作为质押奖励。这些奖励会自动复投或累积,使得金库持有的资产总量增加。
- 如何传递给用户 :同样,通过份额价值的增长来实现。
C. 交易费
这通常涉及提供流动性。
- 运作方式:金库将用户存入的资产配对后,存入去中心化交易所(DEX)的流动性池(如 Uniswap、Curve)。
- 收益来源:每当其他用户在该交易对上进行兑换时,需要支付一小笔手续费。这笔手续费会按比例分配给所有流动性提供者,并累积在池子里。
- 如何传递给用户 :金库在流动性池中赚取的手续费,会不断增加金库的总资产价值。如果期间产生了无常损失,最终收益 = 手续费收入------无常损失。如果前者大于后者,金库净值增长,份额增值;反之则亏损。
总结核心机制
无论金库采用哪种策略,其核心逻辑都是一样的:
- 金库汇集资产:把所有人的钱放在一个篮子里。
- 策略执行:金库的智能合约(或管理者)将这笔钱投入到借贷、质押或做市等活动中。
- 资产增长:这些活动产生的收益(利息、质押奖励、手续费)会使金库的总资产数额(Total Assets)增加。
- 份额升值 :ERC-4626 通过
totalAssets() / totalSupply()计算汇率。总资产增加了,你的份额也就更值钱了。
流动性质押
流动性质押和传统质押最大的区别在于:它让你在赚取质押收益的同时,资产依然是"活"的,可以随时拿去干别的事。 下面我分两部分来解释。
一、流动性质押是什么?为什么需要它?
1. 传统质押的痛点
在以太坊这样的 PoS 区块链上,质押是把你的 ETH 锁定在网络里,成为验证者来维护网络安全,以此获得增发的代币奖励。
但传统质押有个致命问题:资产被锁死。
- 质押后,你的 ETH 无法流动、无法交易、无法用于其他 DeFi 应用
- 在以太坊上赎回需要排队等待,短则几天,长则数周
- 如果你想自己当验证者,门槛是 32 ETH(按当前价格约 6-7 万美元),普通用户根本够不着
这就导致了一个困境:想赚收益,就得牺牲流动性;想要流动性,就得放弃收益。
2. 流动性质押的解决方案
流动性质押协议(如 Lido、Rocket Pool)完美解决了这个矛盾。
核心机制:你把 ETH 存入协议 → 协议替你质押到以太坊网络 → 协议发给你一个"质押凭证代币"(如 stETH),代表你的质押份额和收益权。
这个凭证代币就是关键------它是一个标准的 ERC-20 代币,可以:
- 在交易所直接卖出(即时退出,无需排队等待)
- 作为抵押品去 Aave 等借贷平台借钱
- 存入流动性池赚交易费
- 转到其他协议继续生息
简单说:你一份资产,赚两份钱。 底层资产在赚质押奖励,凭证代币在 DeFi 里继续钱生钱。
二、收益是怎么来的?"自动复投"是什么意思?
1. 质押收益的来源
当你通过 Lido 这样的协议质押 ETH 时,你的 ETH 被委托给专业的节点运营者。这些节点在以太坊网络上负责验证交易、打包区块,因此获得:
- 区块奖励:网络增发的新 ETH
- 交易手续费:用户支付给验证者的 Gas 费
- MEV 奖励:通过优化交易排序获得的额外收益
这些奖励汇总到 Lido 协议,扣除约 10% 的手续费(分给节点运营者和协议国库),剩下的分配给所有 stETH 持有者。
2. 收益如何体现------两种模式
流动性质押代币的收益体现在方式上,主要有两种:
| 模式 | 代表代币 | 运作方式 |
|---|---|---|
| 份额增加型(Rebase) | stETH | 你的代币数量会每天自动增加。比如你今天有 100 个 stETH,明天可能变成 100.01 个 |
| 价格增值型 | rETH、wstETH | 你的代币数量不变,但每个代币兑换底层资产的价格会上升 |
3. "自动复投"是什么意思?
自动复投 指的是:你获得的质押奖励会自动被重新投入新的质押中,继续产生新的收益,整个过程无需你手动操作。
举个直观的例子------假设年化收益是 4%:
| 方式 | 操作 | 一年后结果 |
|---|---|---|
| 无复投 | 每天产生的收益取出来放着 | 本金 10 ETH,收益 0.4 ETH,总共 10.4 ETH |
| 自动复投 | 每天的收益自动加入本金继续生息 | 本金 10 ETH,收益约 0.408 ETH,总共 10.408 ETH |
区别不大?但如果时间拉长到 3 年、5 年,或者收益率更高,复利的威力会非常明显。
在 Lido 的模式中,stETH 采用的就是自动复投,将质押的 ETH 收益继续质押了【简单来说,你的 stETH 余额增加,是因为你最初质押的那笔 ETH 赚到了收益,这些收益被自动重新质押回了 Lido 协议,变成了更多的 stETH】你什么都不用做,躺在钱包里的 stETH 自己就在"利滚利"。
总结
| 概念 | 一句话解释 |
|---|---|
| 流动性质押 | 把资产交给协议替你质押,换回一个可以自由流通的凭证代币 |
| stETH / rETH | 流动性质押凭证,代表你质押的 ETH 及其未来收益 |
| 质押收益来源 | 区块奖励 + 交易手续费 + MEV 收入 |
| 自动复投 | 收益自动加入本金继续生息,无需手动操作 |
| 核心优势 | 收益照拿,资产不锁死,还能在 DeFi 里继续用 |
与 ERC4626 的区别
Lido 的 stETH 于 2020 年推出,而 ERC-4626 标准是在 2023 年 3 月才正式成为以太坊最终标准(Finalized)。stETH 诞生时还没有这个标准。
stETH 是通过增加 份额代币(分红)的形式,拿取收益的。
主要接口差异
ERC-4626 标准规定了存款、取款的统一函数接口:
java
// ERC-4626 标准函数
function deposit(uint256 assets, address receiver) returns (uint256 shares);
function redeem(uint256 shares, address receiver, address owner) returns (uint256 assets);
function withdraw(uint256 assets, address receiver, address owner) returns (uint256 shares);
function mint(uint256 shares, address receiver) returns (uint256 assets);
而 stETH 使用的是不同的接口:
存款:submit(address _referral) 而不是 deposit
取款:目前不支持直接赎回(stETH 需要通过 Curve 等 DEX 卖出变现)
ERC-4626 要求取款时能直接赎回底层资产(如 1 stETH 换回 1 ETH),
但 Lido v1 和 v2 的取款机制都比较复杂,不是简单的"销毁 stETH 返还 ETH"。
这也是它无法完美兼容 4626 的主要原因之一。
ERC4626 常用接口含义
ERC-4626 标准函数:传入与传出参数详解
注意:ERC-4626 的份额(Shares)代币本身也符合 ERC-20 标准,因此以下函数会继承
totalSupply()、balanceOf(address)等标准 ERC-20 函数。
一、核心查询函数
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
asset() |
无 | address assetTokenAddress |
返回底层资产(Asset)代币的合约地址,该代币用于金库的记账、存款和取款 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
totalAssets() |
无 | uint256 totalManagedAssets |
返回金库当前管理的底层资产总额,包括已产生的收益,并已扣除所有费用 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
convertToShares(uint256 assets) |
assets:要转换的底层资产数量 |
uint256 shares |
理想情况 下,存入指定数量的资产可以兑换多少份额。不含费用,不反映滑点,仅用于理论计算 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
convertToAssets(uint256 shares) |
shares:要转换的份额数量 |
uint256 assets |
理想情况 下,赎回指定数量的份额可以获得多少资产。不含费用,不反映滑点 |
二、存款/铸造相关函数(存入资产 → 获得份额)
限额函数
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
maxDeposit(address receiver) |
receiver:接收份额的地址 |
uint256 maxAssets |
该地址单次 deposit 调用最多能存入的资产数量 。若无上限则返回 type(uint256).max |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
maxMint(address receiver) |
receiver:接收份额的地址 |
uint256 maxShares |
该地址单次 mint 调用最多能铸造的份额数量 |
预览函数(模拟)
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
previewDeposit(uint256 assets) |
assets:计划存入的资产数量 |
uint256 shares |
在当前区块条件下,模拟 deposit 调用能获得多少份额。包含存款费用,用于滑点预估 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
previewMint(uint256 shares) |
shares:计划铸造的份额数量 |
uint256 assets |
在当前区块条件下,模拟 mint 调用需要存入多少资产。包含存款费用 |
执行函数
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
deposit(uint256 assets, address receiver) |
assets:要存入的资产数量 receiver:接收份额的地址 |
uint256 shares |
存入指定数量的底层资产,向 receiver 铸造相应数量的份额。调用前需预先批准金库合约使用资产 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
mint(uint256 shares, address receiver) |
shares:要铸造的份额数量 receiver:接收份额的地址 |
uint256 assets |
向 receiver 铸造指定数量的份额,从调用者账户转入相应数量的底层资产 |
三、取款/赎回相关函数(销毁份额 → 取出资产)
限额函数
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
maxWithdraw(address owner) |
owner:份额持有者地址 |
uint256 maxAssets |
该地址最多能提取的资产数量。考虑提款限制或时间锁 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
maxRedeem(address owner) |
owner:份额持有者地址 |
uint256 maxShares |
该地址最多能赎回的份额数量 。通常等于 balanceOf(owner) |
预览函数(模拟)
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
previewWithdraw(uint256 assets) |
assets:计划提取的资产数量 |
uint256 shares |
模拟提取指定数量资产需要销毁多少份额。包含取款费用 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
previewRedeem(uint256 shares) |
shares:计划赎回的份额数量 |
uint256 assets |
模拟赎回指定数量份额能获得多少资产。包含取款费用 |
执行函数
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
withdraw(uint256 assets, address receiver, address owner) |
assets:要提取的资产数量 receiver:接收资产的地址 owner:份额所有者地址 |
uint256 shares |
销毁 owner 的份额,向 receiver 发送指定数量的底层资产。调用者必须是 owner 或被授权者 |
| 函数 | 传入参数 | 传出返回值 | 含义 |
|---|---|---|---|
redeem(uint256 shares, address receiver, address owner) |
shares:要赎回的份额数量 receiver:接收资产的地址 owner:份额所有者地址 |
uint256 assets |
销毁 owner 指定数量的份额,向 receiver 发送相应数量的底层资产 |
四、关于 burn 函数的问题
标准 IERC4626 接口中没有独立的 burn 函数。
份额销毁的功能是通过 withdraw 和 redeem 来实现的:
| 函数 | 调用方式 | 销毁机制 |
|---|---|---|
withdraw(assets, receiver, owner) |
按资产数量提款 | 内部计算需要销毁多少份额,然后调用 _burn |
redeem(shares, receiver, owner) |
按份额数量赎回 | 直接销毁指定数量的份额 |
从实现角度看,以 OpenZeppelin 的 ERC4626 为例,_withdraw 内部函数会调用 _burn(owner, shares) 来销毁份额:
solidity
function _withdraw(...) internal virtual {
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
_burn(owner, shares); // 销毁份额
SafeERC20.safeTransfer(_asset, receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
}
为什么没有独立的 burn?
- 金库的设计理念是资产与份额的原子交换
- 销毁份额必须伴随着资产的转出,不能凭空销毁
- 单独暴露
burn可能导致用户误操作,销毁份额却拿不回资产
五、事件(Events)
| 事件 | 参数 | 含义 |
|---|---|---|
Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares) |
sender:触发存款的地址 owner:接收份额的地址 assets:存入的资产数量 shares:铸造的份额数量 |
通过 deposit 或 mint 存入资产时必须触发 |
| 事件 | 参数 | 含义 |
|---|---|---|
Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares) |
sender:触发取款的地址 receiver:接收资产的地址 owner:份额所有者地址 assets:提取的资产数量 shares:销毁的份额数量 |
通过 withdraw 或 redeem 取出资产时必须触发 |
总结
| 操作类型 | 函数 | 传入参数特点 | 传出返回值 |
|---|---|---|---|
| 存款 | deposit |
指定资产数量,接收份额地址 | 获得的份额数量 |
| 铸造 | mint |
指定份额数量,接收份额地址 | 需要的资产数量 |
| 提款 | withdraw |
指定资产数量,接收方,所有者 | 销毁的份额数量 |
| 赎回 | redeem |
指定份额数量,接收方,所有者 | 获得的资产数量 |
四个核心操作函数中:
deposit和withdraw以资产数量为核心参数mint和redeem以份额数量为核心参数
没有独立的 burn 函数 ,销毁份额通过 withdraw 和 redeem 完成。