
图片原始来源 · 官方文档局部引用 · 公开页面局部截图;未修改页面内容
previewRedeem 返回正数,只说明这笔份额在“假设会被接受”的前提下大约值多少资产,并不说明 redeem 会顺利执行。ERC-4626 的预览函数被要求不把赎回限额算进去,因此额度已经卡住时,它仍会给出一个看似正常的数字。判断能否赎回,应看 maxRedeem(owner),而不是继续盯着预览值。
两个函数分别在回答什么
| 关心的问题 | 对应函数 | 是否考虑限额 | 是否保证成交 |
|---|---|---|---|
| 这些份额大约换多少资产 | previewRedeem(shares) |
不考虑 | 不保证 |
| 该地址此刻最多能赎回多少份额 | maxRedeem(owner) |
考虑全局与用户级限额 | 不保证,只是上限 |
| 按资产数量最多能提多少 | maxWithdraw(owner) |
考虑限额 | 不保证,只是上限 |
| 真正执行 | redeem / withdraw |
执行时再判定 | 不满足条件就回滚 |
按标准,previewRedeem 应尽量接近同一笔交易中 redeem 会返回的资产数,且包含赎回费用;但它必须假设赎回会被接受,不管用户份额够不够、额度是否已满。maxRedeem 则要把全局限额和用户级限额都算进去,赎回被完全暂停(哪怕只是暂时)时应返回 0。两者回答的是不同问题,不能互相替代。
一个明确假设的教学计算
设某金库份额与资产按 1:1.05 估算,你持有 1,000 份,想全部赎回:
previewRedeem(1000)返回约 1,050 个资产单位(含费口径)。maxRedeem(你的地址)只返回 400。
这说明当前额度只允许赎回 400 份。提交 1,000 份的 redeem 会因超过限额而回滚,前端仍显示 1,050,是因为预览函数本来就不查额度。可行做法是把数量降到不超过 maxRedeem,或等限额恢复后再试。
反过来,maxRedeem 足够大也不等于一定成功。份额授权、owner 的实际份额余额、金库自身的其他前置条件,以及交易打包时的状态变化,都可能让执行失败。预览值与成交值的差异,还可能来自手续费、份额价格在区块间变动,或实现自带的特殊规则。
排查顺序
- 先读
maxRedeem(owner)。 返回 0 时,既可能是赎回被暂停或该用户受限,也可能是这个地址根本没有份额——两种情况预览都仍可能给出数字。返回非零但小于你要赎回的份额数,就把数量压到额度以内。 - 核对额度之外的条件。 调用者是本人还是代操作(代操作需要份额授权)、owner 的份额余额是否足够、金库是否要求先完成某些前置操作。
- 最后看预览值与最终结果的偏差。 偏差明显时,优先怀疑费率、价格变动或实现规则,而不是认定预览函数算错。
容易混淆的几点
- max 是上限,不是承诺。 标准要求它不得高于实际可接受的最大值,必要时宁可低估,所以它偏保守;即便数量不超过它,执行时仍可能因状态变化失败。
maxRedeem不会超过 owner 持有的份额。 无上限时返回2**256 - 1的是maxDeposit、maxMint这类存入方向的函数,不是maxRedeem。- 暂停与限额由具体实现决定。 标准只要求赎回被完全禁用时
maxRedeem返回 0,什么情况下暂停、限额多少、是否按用户区分,都取决于该金库的合约逻辑。 maxWithdraw与maxRedeem不能混用。 前者以资产计价,后者以份额计价;按资产数量提款看前者,按份额赎回看后者。- 标准
redeem没有滑点参数。 某些路由或封装合约会另外加保护,但那是外围逻辑,不属于 ERC-4626 的赎回接口。 - 异步提款请求不是所有金库都有。 只有实现方另行设计了该流程时才需要先提交请求,不能默认每个金库都这样。
所以,看到 previewRedeem 有数字却赎回不了,先查 maxRedeem 是否覆盖你要赎回的份额,再排查余额、授权和实现特有的前置条件,通常就能定位原因。