Q1ngying

今朝梦醒与君别,遥盼春风寄相思

0%

MakerDAO-Vat

MakerDAO Vat 合约分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
// 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);
}
}