跳到主要内容

TUNDRA 借贷协议

本页讲的是借贷(TUNDRA)背后的机制。读取市场数据和构建交易,请看开发者指南;所有合约 ID 见合约参考。

TUNDRA 是运行在 alkanes 上的 Morpho Blue 分叉。凡是 alkanes 能支持的 Morpho Blue 行为,都原样复现,常量也保持一致;alkanes 不支持的地方,下文逐条说明差异。TUNDRA 最看重保留的是 Morpho Blue 的信任模型:任何人都可以创建市场,市场的风险参数永久固定,治理无法暂停、没收或重新调整任何已上线的东西。

架构​

               TUNDRA 工厂合约 4:881050
(登记表:市场 ID -> 资金池,启用列表,费用设置)
| |
资金池 2:98445 资金池 2:98619
DIESEL -> frUSD 市场 DIESEL -> frBTC 市场
只保管本市场的代币 只保管本市场的代币
| | | |
预言机 4:727967 利率模型 4:674184 预言机 4:727968
\______________ TUNDRACLE 4:9010 _____/

工厂合约​

工厂合约(4:881050)负责四件事:

  • 创建市场。 CreateMarket 无需许可。它为新市场克隆资金池模板(4:668244),初始化并校验这个克隆,然后在登记表里记录 市场 ID -> 资金池。
  • 维护启用列表。 市场只能使用治理已启用的利率模型(IRM)和 LLTV。启用是单向的:一旦启用,就不能再停用。
  • 保存费用设置。 新市场的默认费率(目前为 10%)和费用接收方。
  • 回答市场发现查询。 GetPool(市场 ID) 是判断哪个合约服务某个市场的唯一权威。

工厂合约不保管用户资金,也不提供用户操作。用户直接调用资金池。

每个市场一个资金池​

Morpho Blue 原版是一个合约容纳所有市场。TUNDRA 则为每个市场克隆一个独立的资金池合约。资金池只保管本市场的代币,承载全部用户操作:存款、取款、借款、还款、追加抵押品、关闭仓位、清算和闪电贷。

这是与原版最大的不同,换来的是隔离:账本漏洞只会影响一个市场的代币,而不是所有市场;闪电贷也只能借出这一个资金池持有的代币。信任模型没有改变。

资金池份额就是存款凭证​

每个资金池同时也是它自己的代币。存款时,资金池给存款人铸造自己的份额;取款时销毁份额。没有单独的份额代币合约,也没有外部可调用的铸造或销毁操作码。

利息让资金池的存款总额增加,而份额总数不变,所以每一份额能赎回的代币越来越多。持有份额就是索取权:存款端无需登记,份额可以像任何 alkane 一样转账或由其他合约持有。

新资金池以「每 1 个借出代币最小单位对应 1,000,000 份额」开局(即 Morpho Blue 的虚拟份额机制,用来防御针对空资金池的「通胀攻击」)。资金池名称为 Tundra Pool <市场 ID 前 8 位十六进制>,代号 TUN-P。

治理是一枚不记名代币​

治理权是一枚不可分割的代币:工厂合约自身 ID 4:881050 的一个单位,在工厂部署时铸造。持有承载它的 UTXO 就是拥有治理权,转交它就是花费它。同一枚代币也用于授权所有资金池上的 SetFee。

治理能做的事:

  • 启用利率模型;
  • 启用 LLTV;
  • 设置某个市场的费率,最高为利息的 25%;
  • 设置之后新建市场的默认费率;
  • 指定费用接收方。

治理不能做的事:暂停市场、没收或挪动仓位、修改已上线市场的预言机、利率模型、LLTV 或最低借款额、升级资金池、阻止清算。即使治理代币丢失,所有市场也会照常运行,只是启用列表和费用设置从此冻结。

目前启用的 LLTV 为 35% 和 40%,启用的利率模型是 4:674184 上的自适应曲线模型。

市场与市场 ID​

一个市场由六个参数定义,编码为十个小端序 u128 字:

字参数
1-2借出代币(block、tx)
3-4抵押品代币
5-6预言机
7-8利率模型
9LLTV,以 1e18 为精度
10最低借款额,以借出代币最小单位计(0 表示不限制)

市场 ID 是这 160 字节的 SHA-256 哈希(Morpho Blue 用 keccak256,alkanes 只有 SHA-256)。只要参数一致,两个人得到的就是同一个市场,无需协调;任何参数一变,就是另一个市场。这也是参数无法修改的原因:「修改后」的市场其实就是另一个市场。

最低借款额是 TUNDRA 新增的参数,原版只有五个字段。见最低借款额。

市场发现规则​

任何人都可以用编造的参数克隆资金池模板,假资金池可以按创建者的意愿回答任何查询。只有同时满足以下两个条件,资金池才是真正的 TUNDRA 市场:

pool.GetFactory() == 4:881050
factory.GetPool(pool.GetMarketId()) == pool

SUBFROST 应用和清算机器人都会拒绝没有通过这项检查的资金池。

账户与仓位​

alkanes 合约无法直接向地址付款。它把代币放在返回结果里,由运行时转到交易中 pointer 所指定的输出。TUNDRA 的所有权模型就建立在这一点上。

  • 存款人持有资金池份额,不需要账户。
  • 借款人拥有一个仓位,以「所有者 + salt」为键。所有者可以是:
    • 一个脚本(比特币输出脚本,比如 taproot 地址)。个人借款用的就是这种方式。salt 必须为 0,所以每个脚本在每个市场只有一个仓位。
    • 一个 alkane(合约)。合约可以用 salt 维护多个仓位。

仓位键的计算方式:

key = sha256(owner_encoding || salt,16 字节小端序)
owner_encoding = 0x01 || 脚本长度(1 字节)|| 脚本 (脚本所有者)
= 0x00 || block(16 字节小端序)|| tx(16 字节小端序)(alkane 所有者)

脚本所有者的仓位通过证明付款落在该脚本上来授权:资金池读取自己所在的交易,找到自己的 protostone,检查其 pointer 是否指向一个真实输出,且该输出的脚本正是所有者的脚本。以下三条规则保证了它的安全:

  • 只认顶层调用。 脚本所有者的授权只在顶层 protostone 中有效,因为只有顶层才会按 pointer 路由。合约不能代替脚本所有者操作;合约若要管理仓位,必须以 alkane 所有者身份持有。
  • 授权消息不能带 edict。 运行时会在路由返回结果之后才执行该 protostone 的 edict,edict 可能在检查通过后把款项从所有者那里转走。所以资金池拒绝任何带 edict 的授权消息。实际操作中,代币由前一个只含 edict 的 protostone 转入调用。
  • pointer 必须指向真实输出,而不是后面的某个 protostone。

各项操作所需的授权:

操作对所有者的影响授权方式
存款、追加抵押品、还款资产流入,健康度提高无需授权
取款销毁调用者发来的份额份额本身
开仓或追加借款欠款增加借出的代币必须付给所有者(pointer)
关闭仓位(欠款为零)退回抵押品抵押品必须付给所有者(pointer)
清算与原版一样无需许可无需授权
闪电贷原子操作,不产生仓位无需授权

追加借款。 同一所有者的第二次借款会并入现有仓位,并且只有满足以下条件才会被接受:

  1. 不抬高仓位的欠款与抵押品之比。设现有欠款 D、抵押品 C,追加欠款 d、抵押品 c,要求 d × C <= D × c。这个条件不依赖价格,所以在任何价格下都成立;
  2. 合并后的仓位在预言机价格下是健康的。

两者结合,使得替别人的仓位借款毫无意义:借出的钱归所有者,而所有者的比例不会变差。

不支持部分提取抵押品。 抵押品只能通过 ClosePosition(全部取回,且欠款必须为零)或清算离开仓位。想减少抵押品的借款人,需要先还款、关闭,再重新开仓。

利息​

利息按区块累积,因为比特币上的合约能读到区块高度,却读不到时钟。所有利率都是每区块利率,一年按 52,560 个区块计。比特币出块通常略快于每十分钟一个,所以借款人实际支付的利息会比名义年利率略高一点,存款人也会多拿到同样的一点。

自适应曲线利率模型​

4:674184 就是 Morpho Blue 的 AdaptiveCurveIRM,常量完全相同:

常量数值
目标利用率 u*90%
曲线陡度 k4
初始目标利率年化 4%
目标利率范围年化 0.1% 到 200%
调整速度每年 50

设利用率为 u,归一化偏差为:

e = (u − u*) / u*          当 u <= u*
e = (u − u*) / (1 − u*) 当 u > u*

借款利率是当前目标利率 r_t 的倍数:

r = r_t × (1 + (1 − 1/k) × e)    当 e < 0     (u = 0 时为 r_t / 4)
r = r_t × (1 + (k − 1) × e) 当 e >= 0 (u = 1 时为 4 × r_t)

两次计息之间,目标利率按指数调整:

r_t(新)= r_t × exp(调整速度 × e × 经过的区块数 / 52,560)

并被限制在上述范围内。e = ±1 时,r_t 大约每五天翻倍或减半。一段时间内收取的利率是沿这条路径的平均值,所以两次计息隔得久,与多次短间隔计息的成本相同。利率模型为每个调用它的资金池分别保存目标利率。

计息与费用​

存款、取款、借款、还款和清算都会先计息。以每区块利率 r 经过 n 个区块,利息为 借款总额 × (e^(r·n) − 1),用 Morpho Blue 的三阶泰勒展开计算。利息同时加到借款总额和存款总额上。

费用从每次计息中抽取一部分,以新铸造的资金池份额形式给费用接收方,份额价值恰好等于费用金额,因此不会稀释自身。接收方通过 ClaimFee 领取。两个已上线市场都以工厂默认的 10% 起步,上限为 25%。

存款利率 = 借款利率 × 利用率 × (1 − 费率)

健康度与清算​

健康度​

价格使用预言机的精度:每单位抵押品最小单位可换得的借出代币最小单位数,乘以 1e27。

最大可借 = 抵押品 × 价格 / 1e27 × LLTV
健康 ⇔ 欠款 <= 最大可借

欠款由仓位的借款份额换算,向上取整。没有欠款的仓位永远是健康的。

清算​

任何人都可以清算不健康的仓位。清算人指定要拿走多少抵押品,或要偿还多少借款份额,另一个数值用清算激励系数算出:

LIF = min(1.15, 1 / (1 − 0.5 × (1 − LLTV)))
拿走的抵押品价值 = 偿还的欠款 × LIF

原版公式中的系数(cursor)是 0.3,TUNDRA 用的是 0.5。因为比特币上的清算人无法在一个区块内反应,每次尝试都要付真实的手续费,还可能在打包竞争中落败,所以奖励必须覆盖一笔固定成本,而不是一笔可以被竞争压低的成本。1.15 的上限沿用原版。

LLTVLIF清算奖励
40%1.15(触顶)15.00%
77%1.13012.99%
86%1.0757.53%

两个已上线市场的 LLTV 都是 40%,奖励固定为 15%。

没有清算比例上限:一次调用就可以把一个仓位整笔清算。如果清算拿走了仓位最后的抵押品而仍有欠款,剩余欠款就是坏账,会在同一次调用中通过减少存款总额计入存款人。没有保险基金。

缓冲。 处在上限的仓位,在价格跌到「刚变为可清算时价格」的 LLTV × LIF 以下时,就无法被完全清偿。从那一刻起的安全跌幅为 1 − LLTV × LIF:LLTV 为 40% 时是 54%。

回调。 Liquidate 可以带一个可选的 callee。设置后,资金池先结算自身状态,把拿走的抵押品发给 callee,再从 callee 返回的代币中扣除还款;callee 返回的其他代币都归清算人。这样,手里没有借出代币的清算人也能在同一笔交易里卖掉抵押品并还款。4:999999 就是这样一个 callee:它通过 AMM 工厂 4:65522 卖出抵押品,如果卖出所得不足以还款,整笔清算会回滚。见运行清算机器人。

最低借款额​

太小而不值得清算的仓位,会在暴跌中变成坏账。TUNDRA 增加了第六个市场参数:最低借款额,并在链上强制执行:

  • 开仓和追加借款时,按仓位总欠款检查;
  • 部分还款时,最多还到正好剩下最低借款额,多出的部分退回;除非这次还款清掉全部欠款;
  • 部分清算时,剩余欠款不能低于最低借款额。整笔清算,或拿走全部抵押品的清算,始终允许。

已上线市场的最低借款额(3,000 frUSD 和 0.035 frBTC)是这样定的:15% 的奖励要能覆盖三次清算尝试,每次按暴跌时约 450 vB、300 sat/vB 的手续费计算。

LLTV 是怎么选的​

上线第一个市场之前,团队用 347 天的 DIESEL / frBTC 逐小时价格(8,322 根 K 线),统计每个候选 LLTV 的缓冲在一到八小时内被击穿的次数。LLTV 为 40% 时,两小时内没有任何一次击穿;回放样本中最严重的一次暴跌(下跌 57.5%),清算也能干净完成。60% 及以上的 LLTV 在同一样本中多次被击穿。治理只启用了 35% 和 40%。

预言机​

TUNDRA 沿用 Morpho Blue 的做法,每个市场只用一个预言机函数:price(),操作码 2,返回每单位抵押品可换得的借出代币数量,精度 1e27。每个市场指定自己的预言机适配合约,两个已上线的适配合约都读取 TUNDRACLE(4:9010):

适配合约读取价格
4:727967TUNDRACLE 市场 4以美元计价的 DIESEL,用作每枚 DIESEL 的 frUSD 价格
4:727968TUNDRACLE 市场 5DIESEL / frBTC,两个价格源之比

TUNDRACLE 的精度是 1e8,所以每个适配合约乘以 1e19 换算到 1e27。适配合约的设置在部署时就已固定,因为它的 ID 是市场 ID 的一部分。

价格从哪里来​

TUNDRACLE 订阅 SUBFROST 的预言机 DOOMACLE(4:9000)。价格由 DOOMACLE 签名组写入 DOOMACLE。这是一个 6-of-9 的 FROST 门限签名组,地址为 bc1pp9xj9ww24x5wenae96dpj3jtxnvrwehn4gqtam7xfc0dfwcmn2rqnms0du。九名签名者中必须有六名对同一笔交易达成一致,价格才会写入。

价格直接取自比特币链上的 AMM 资金池,所以每个签名者都能从相同的链上状态算出相同的数值:

  • BTC 取自 frBTC / frUSD 资金池(4:1778);
  • DIESEL 取自 DIESEL / frBTC 资金池(2:77087),再乘以 BTC 价格;
  • frBTC 按托管锚定等于 BTC;
  • frUSD 固定为 $1。

每个价格是 12 个样本的中位数,样本间隔 12 个区块,窗口共 144 个区块(约一天)。中位数会忽略单个被操纵的样本,所以只在一个区块里拉动资金池价格,无法改变最终价格。代价是滞后:真实的价格变动会逐步反映到预言机中。

新鲜度检查​

TUNDRACLE 的每个价格源如果超过 144 个区块没有更新,就被视为过期。比值类市场(例如 DIESEL / frBTC)只有在两个价格源都新鲜时才算新鲜。

TUNDRACLE 会保留过期价格,而不是把它归零,因为价格归零会让所有仓位瞬间可被清算。所以 TUNDRA 的适配合约在价格不新鲜时会直接回滚。对资金池的影响如下:

操作是否需要价格价格过期时
开仓或追加借款需要拒绝
清算需要拒绝
存款、取款不需要正常
还款、追加抵押品、关闭仓位不需要正常

因此,价格源过期的市场会冻结新增风险,但始终可以平仓退出。

费用​

费用数值由谁设定
利息上的协议费两个市场均为 10%治理,每个市场最高 25%
新市场默认费率10%治理
存款、取款、借款、还款除比特币网络手续费外没有其他费用
闪电贷免费

费用接收方登记在工厂合约上(GetFeeRecipient,操作码 107),具体地址见合约参考。

与 Morpho Blue 的对比​

Morpho BlueTUNDRA
结构单一合约工厂合约 + 每个市场一个资金池
存款仓位账本中的份额资金池自身的代币(资金池份额)
账户以太坊地址比特币脚本(以款项落点授权)或 alkane(以调用者授权)
市场参数5 个6 个:新增最低借款额
市场 IDkeccak256SHA-256
时间秒区块(一年 52,560 个)
预言机精度1e361e27(1e36 放不进 128 位整数)
清算系数(cursor)0.30.5
向某仓位追加借款所有者或授权操作者任何人都可以,但不能抬高欠款比例,且借款归所有者
部分提取抵押品支持不支持,需关闭后重开
治理一个所有者地址一枚不记名代币
新市场费率设定前为 0以默认费率起步
保持不变25% 最高费率、1.15 最高激励、虚拟份额、取整方向、自适应利率模型及其常量、坏账即时计入、无需许可创建市场、参数永久固定

原版的 MetaMorpho 金库层不属于 TUNDRA。

接下来看什么​