MakerDAO-Vat
MakerDAO Vat 合约分析
// SPDX-License-Identifier: AGPL-3.0-or-later
/// vat.sol -- Dai CDP database
// Copyright (C) 2018 Rain <rainbreak@riseup.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.6.12;
// FIXME: This contract was altered compared to the production version.
// It doesn't use LibNote anymore.
// New deployments of this contract will need to include custom events (TO DO).
/**
* @title
* @author
* @notice
* - `Vat` 合约中的 Dai 不是以 ERC-20 代币的形式存在的,而是作为系统内部的记账单位 `rad` 存在的
* 这些 Dai 只在系统内部使用,不在市场上直接流通,设计原因如下:
* - 系统内部记账:Vat 合约使用 rad 作为记账单位,确保高精度(10^45)记录所有抵押品和债务。
* - 管理和操作:通过将 Dai 转换为内部记账单位,系统可以更容易地管理和操作抵押品和生成的 Dai。
* - 流通和流动性:在需要时,通过 DaiJoin 的 exit 操作,用户可以将 Vat 中的 Dai 转换回 ERC-20
* 形式的 Dai,从而进入市场流通。
*/
contract Vat {
// --- Auth ---
mapping(address => uint256) public wards;
function rely(address usr) external auth {
require(live == 1, "Vat/not-live");
wards[usr] = 1;
}
function deny(address usr) external auth {
require(live == 1, "Vat/not-live");
wards[usr] = 0;
}
modifier auth() {
require(wards[msg.sender] == 1, "Vat/not-authorized");
_;
}
mapping(address => mapping(address => uint256)) public can;
function hope(address usr) external {
can[msg.sender][usr] = 1;
}
function nope(address usr) external {
can[msg.sender][usr] = 0;
}
function wish(address bit, address usr) internal view returns (bool) {
return either(bit == usr, can[bit][usr] == 1);
}
// --- Data ---
struct Ilk {
// 抵押类型
uint256 Art; // Total Normalised Debt [wad] 总标准化债务 [wad = 10^18]
uint256 rate; // Accumulated Rates [ray] 累计费率 [ray = 10^27]
uint256 spot; // Price with Safety Margin [ray] 带有安全边际的价格 [ray = 10^27]
uint256 line; // Debt Ceiling [rad] 债务上限 [rad = 10^45]
uint256 dust; // Urn Debt Floor [rad] 债务下限 [rad = 10^45]
}
struct Urn {
// 一个特殊的 Vault
uint256 ink; // Locked Collateral [wad = 10^18] 抵押品金额
uint256 art; // Normalised Debt [wad = 10^18] 标准化未偿还稳定币债务
}
// --- Mappings ---
/// @notice 存储每种抵押品类型(ilk)的相关信息。
mapping(bytes32 => Ilk) public ilks;
/**
* @notice 存储每种抵押品类型下,每个用户的 Vault 信息(即每个用户持有的特定抵押品的详细信息)。
* - `bytes32` 抵押品类型标识符
* - `address` 用户地址
*/
mapping(bytes32 => mapping(address => Urn)) public urns;
/// @notice 用于存储每种抵押品类型下,每个用户的抵押品余额。
mapping(bytes32 => mapping(address => uint256)) public gem; // [wad = 10^18]
mapping(address => uint256) public dai; // [rad = 10^45]
mapping(address => uint256) public sin; // [rad = 10^45]
uint256 public debt; // Total Dai Issued [rad = 10^45] 总债务
uint256 public vice; // Total Unbacked Dai [rad = 10^45] 总不良债务
uint256 public Line; // Total Debt Ceiling [rad = 10^45] 总债务上限
uint256 public live; // Active Flag
// --- Init ---
constructor() public {
wards[msg.sender] = 1;
live = 1;
}
// --- Math ---
function _add(uint256 x, int256 y) internal pure returns (uint256 z) {
z = x + uint256(y);
require(y >= 0 || z <= x);
require(y <= 0 || z >= x);
}
function _sub(uint256 x, int256 y) internal pure returns (uint256 z) {
z = x - uint256(y);
require(y <= 0 || z <= x);
require(y >= 0 || z >= x);
}
function _mul(uint256 x, int256 y) internal pure returns (int256 z) {
z = int256(x) * y;
require(int256(x) >= 0);
require(y == 0 || z / y == int256(x));
}
function _add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function _sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x);
}
// --- Administration ---
/**
* @notice 初始化新的抵押品类型(`ilk`)
* @dev 该函数确保了一个 `ilk` 只能初始化一次,并在初始化时设置其初始费率
* @dev `auth` 修饰符确保了这个函数的权限访问,只能具有授权的用户调用
* @param ilk 抵押品类型
*/
function init(bytes32 ilk) external auth {
require(ilks[ilk].rate == 0, "Vat/ilk-already-init"); // 是否已经初始化,若已经初始化,直接回滚
ilks[ilk].rate = 10 ** 27; // 初始化抵押品的费率为 1 ray [ray = 10^27]
}
/**
* @notice 用于设置全局参数,例如系统的债务上限(`Line`)
* @dev `auth` 修饰符确保了这个函数的权限访问,只能具有授权的用户调用
* @param what 用于指定更新的参数名称,该函数中 `what` 可以是 `Line` 表示系统的总债务上限
* @param data 指定 `what` 参数对应的新值,该函数中,`data` 是系统的总债务上限的具体数值
*/
function file(bytes32 what, uint256 data) external auth {
require(live == 1, "Vat/not-live");
if (what == "Line") Line = data;
else revert("Vat/file-unrecognized-param");
}
/**
* @notice 用于设置特定抵押品类型的参数,例如现货价格(spot)、债务上限(line)和最小债务(dust)。
* @param ilk 指定要更新的抵押品类型
* @param what 指定要更新的具体参数名称,该函数中,可选值有 `spot`、`line`、`dust`,分别表示现货价格、债务上限和最小债务。
* @param data 指定 `what` 参数对应的新值
*/
function file(bytes32 ilk, bytes32 what, uint256 data) external auth {
require(live == 1, "Vat/not-live");
if (what == "spot") ilks[ilk].spot = data;
else if (what == "line") ilks[ilk].line = data;
else if (what == "dust") ilks[ilk].dust = data;
else revert("Vat/file-unrecognized-param");
}
function cage() external auth {
live = 0;
}
// --- Fungibility --- (可互换性)
/**
* 修改用户抵押品的金额,admin
* @param ilk 抵押品
* @param usr 用户
* @param wad 金额
*/
function slip(bytes32 ilk, address usr, int256 wad) external auth {
gem[ilk][usr] = _add(gem[ilk][usr], wad);
}
/**
* 用户之间转移抵押品
* @param ilk 抵押品
* @param src 转出方
* @param dst 转入到
* @param wad 金额
*/
function flux(bytes32 ilk, address src, address dst, uint256 wad) external {
require(wish(src, msg.sender), "Vat/not-allowed");
gem[ilk][src] = _sub(gem[ilk][src], wad);
gem[ilk][dst] = _add(gem[ilk][dst], wad);
}
/**
* 在用户之间转移稳定币
* @param src 转出方用户地址
* @param dst 转入到用户地址
* @param rad 金额 [rad = 10^45]
*/
function move(address src, address dst, uint256 rad) external {
require(wish(src, msg.sender), "Vat/not-allowed");
dai[src] = _sub(dai[src], rad);
dai[dst] = _add(dai[dst], rad);
}
function either(bool x, bool y) internal pure returns (bool z) {
assembly {
z := or(x, y)
}
}
function both(bool x, bool y) internal pure returns (bool z) {
assembly {
z := and(x, y)
}
}
// --- CDP Manipulation --- (CDP 操纵)
/**
* 修改 Vault
* @param i <=> ilk 资产类型
* @param u <=> 对应的特殊 Vault
* @param v <=> 授权检查地址(与抵押品操作相关)
* @param w <=> Dai 债务的目标地址
* @param dink 抵押品变化
* @param dart 债务变化
* @notice `dink` 为正值时,表示增加抵押品,抵押品从 `v` 地址转到 Vault,为负值,表示减少抵押品,从 Vault 转到 `v`
* `dink` 为正值时,还需要检查 `v` 地址是否授权给 `msg.sender` 操作其抵押品
* `dart` 为正值时,表示增加债务,生成的 Dai 发送到 `w` 地址。为负值时,表示偿还债务,`w` 地址的 Dai 被扣除
* `dart` 为负值时,还需要检查 `w` 地址是否授权给 `msg.sender` 操作其 Dai
*/
function frob(bytes32 i, address u, address v, address w, int256 dink, int256 dart) external {
// system is live
require(live == 1, "Vat/not-live");
Urn memory urn = urns[i][u];
Ilk memory ilk = ilks[i];
// ilk has been initialised
require(ilk.rate != 0, "Vat/ilk-not-init");
urn.ink = _add(urn.ink, dink);
urn.art = _add(urn.art, dart);
ilk.Art = _add(ilk.Art, dart);
int256 dtab = _mul(ilk.rate, dart);
uint256 tab = _mul(ilk.rate, urn.art);
debt = _add(debt, dtab);
// either debt has decreased, or debt ceilings are not exceeded 确保债务没有增加或没有超过债务上限
require(either(dart <= 0, both(_mul(ilk.Art, ilk.rate) <= ilk.line, debt <= Line)), "Vat/ceiling-exceeded");
// urn is either less risky than before, or it is safe 确保 Vault 的风险降低或仍然安全
require(either(both(dart <= 0, dink >= 0), tab <= _mul(urn.ink, ilk.spot)), "Vat/not-safe");
// urn is either more safe, or the owner consents 确保 Vault 更加安全,或者所有者同意
require(either(both(dart <= 0, dink >= 0), wish(u, msg.sender)), "Vat/not-allowed-u");
// collateral src consents 确保抵押品来源地址同意
require(either(dink <= 0, wish(v, msg.sender)), "Vat/not-allowed-v");
// debt dst consents 确保债务目标地址同意
require(either(dart >= 0, wish(w, msg.sender)), "Vat/not-allowed-w");
// urn has no debt, or a non-dusty amount 确保 Vault 没有债务,或者债务大于最低限额
require(either(urn.art == 0, tab >= ilk.dust), "Vat/dust");
gem[i][v] = _sub(gem[i][v], dink);
dai[w] = _add(dai[w], dtab);
urns[i][u] = urn;
ilks[i] = ilk;
}
// --- CDP Fungibility ---
/**
* @notice 用于在两个 Vault 之间转移抵押品和债务。具体来说,它可以将一个 Vault 中的部分抵押品和债务拆分出来,转移到另一个
* Vault 中
* @param ilk 资产类型
* @param src 抵押品来源 (源 Vault)
* @param dst 债务目标 (目标 Vault)
* @param dink 抵押品变化
* @param dart 债务变化
*/
function fork(bytes32 ilk, address src, address dst, int256 dink, int256 dart) external {
Urn storage u = urns[ilk][src];
Urn storage v = urns[ilk][dst];
Ilk storage i = ilks[ilk];
u.ink = _sub(u.ink, dink);
u.art = _sub(u.art, dart);
v.ink = _add(v.ink, dink);
v.art = _add(v.art, dart);
uint256 utab = _mul(u.art, i.rate);
uint256 vtab = _mul(v.art, i.rate);
// both sides consent
require(both(wish(src, msg.sender), wish(dst, msg.sender)), "Vat/not-allowed");
// both sides safe
require(utab <= _mul(u.ink, i.spot), "Vat/not-safe-src");
require(vtab <= _mul(v.ink, i.spot), "Vat/not-safe-dst");
// both sides non-dusty
require(either(utab >= i.dust, u.art == 0), "Vat/dust-src");
require(either(vtab >= i.dust, v.art == 0), "Vat/dust-dst");
}
// --- CDP Confiscation ---
/**
* 清算 Vault
* @param i 抵押品类型
* @param u Vault 地址
* @param v 抵押品的目标地址
* @param w 债务的目标地址
* @param dink 抵押品的变化量
* @param dart 债务的变化量
*/
function grab(bytes32 i, address u, address v, address w, int256 dink, int256 dart) external auth {
Urn storage urn = urns[i][u];
Ilk storage ilk = ilks[i];
urn.ink = _add(urn.ink, dink);
urn.art = _add(urn.art, dart);
ilk.Art = _add(ilk.Art, dart);
int256 dtab = _mul(ilk.rate, dart);
gem[i][v] = _sub(gem[i][v], dink);
sin[w] = _sub(sin[w], dtab);
vice = _sub(vice, dtab);
}
// --- Settlement ---
/**
* 抵消用户的 `sin`(sin:代表"扣押"或"坏"债务)和负债
* @param rad 抵消的金额
*/
function heal(uint256 rad) external {
address u = msg.sender;
sin[u] = _sub(sin[u], rad);
dai[u] = _sub(dai[u], rad);
vice = _sub(vice, rad);
debt = _sub(debt, rad);
}
/**
* 增加系统中的债务和不良债务,同时向特定用户分解 Dai
* @param u 增加 `sin` 的用户地址
* @param v 增加 Dai 的用户地址
* @param rad 增加的金额
*/
function suck(address u, address v, uint256 rad) external auth {
sin[u] = _add(sin[u], rad);
dai[v] = _add(dai[v], rad);
vice = _add(vice, rad);
debt = _add(debt, rad);
}
// --- Rates ---
/**
* 调整特定抵押品类型的费率,并更新系统中的 Dai 和债务
* @param i 抵押品类型(ilk)
* @param u 更新 Dai 余额的用户地址
* @param rate 费率的变化,单位为 `ray` 10^27
*/
function fold(bytes32 i, address u, int256 rate) external auth {
require(live == 1, "Vat/not-live");
Ilk storage ilk = ilks[i];
ilk.rate = _add(ilk.rate, rate);
int256 rad = _mul(ilk.Art, rate);
dai[u] = _add(dai[u], rad);
debt = _add(debt, rad);
}
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Q1ngying!