Q1ngying

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

0%

MakerDAO-Join

MakerDAO Join 合约分析

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
// SPDX-License-Identifier: AGPL-3.0-or-later

/// join.sol -- Basic token adapters 基本 Token 适配器

// 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).

interface GemLike {
function decimals() external view returns (uint256);
function transfer(address, uint256) external returns (bool);
function transferFrom(address, address, uint256) external returns (bool);
}

interface DSTokenLike {
function mint(address, uint256) external;
function burn(address, uint256) external;
}

interface VatLike {
function slip(bytes32, address, int256) external;
function move(address, address, uint256) external;
}

/*
Here we provide *adapters* to connect the Vat to arbitrary external
token implementations, creating a bounded context for the Vat. The
adapters here are provided as working examples:

- `GemJoin`: For well behaved ERC20 tokens, with simple transfer
semantics.

- `ETHJoin`: For native Ether.

- `DaiJoin`: For connecting internal Dai balances to an external
`DSToken` implementation.

In practice, adapter implementations will be varied and specific to
individual collateral types, accounting for different transfer
semantics and token standards.

Adapters need to implement two basic methods:

- `join`: enter collateral into the system
- `exit`: remove collateral from the system

*/

/**
* @title
* @author
* @notice
* - 当用户想要将抵押品存入/取出系统(`Vat`)时,与该合约交互
* - 每一种 ERC20 Token 对应一个 Gemjoin 合约
*/
contract GemJoin {
// --- Auth ---
mapping(address => uint256) public wards;

function rely(address usr) external auth {
wards[usr] = 1;
emit Rely(usr);
}

function deny(address usr) external auth {
wards[usr] = 0;
emit Deny(usr);
}

modifier auth() {
require(wards[msg.sender] == 1, "GemJoin/not-authorized");
_;
}

VatLike public vat; // CDP Engine
bytes32 public ilk; // Collateral Type
GemLike public gem;
uint256 public dec;
uint256 public live; // Active Flag

// Events
event Rely(address indexed usr);
event Deny(address indexed usr);
event Join(address indexed usr, uint256 wad);
event Exit(address indexed usr, uint256 wad);
event Cage();

constructor(address vat_, bytes32 ilk_, address gem_) public {
wards[msg.sender] = 1;
live = 1;
vat = VatLike(vat_);
ilk = ilk_;
gem = GemLike(gem_);
dec = gem.decimals();
emit Rely(msg.sender);
}

/**
* @notice
* - 修改 GemJoin 合约的状态,适配器排空(不允许 `join`,但是允许 `exit`)
* - `join` 受限:防止在系统非活跃状态下增加新的抵押品,从而避免系统风险增加。
* - `exit` 不受限:用户可以在任何时候取回他们的抵押品,确保用户资产的自由流动。
*/
function cage() external auth {
live = 0;
emit Cage();
}

/**
* @notice 将用户的 ERC-20 代币存入系统(消耗 `msg.sender` 的 Token,增加 `usr` 的抵押品数量)
* @param usr 存入抵押品的地址(若为 `msg.sender` 自己,则 `usr = msg.sender`
* @param wad 存入的代币数量,单位 wad (10^18)
* @notice
* - 此函数用于将用户的 ERC-20 代币存入 MakerDAO 系统,并记录到 `Vat` 合约中
* - 用户存入代币时需要确保系统处于活跃状态(`live == 1`),并且代币数量非负(`int256(wad) >= 0`)
* - 成功存入代币后,触发 `Join` 事件
* - `msg.sender` -> `vat[usr]` (`msg.sender`把抵押品存入合约,视为 `usr` 的抵押品)
*/
function join(address usr, uint256 wad) external {
require(live == 1, "GemJoin/not-live");
require(int256(wad) >= 0, "GemJoin/overflow");
vat.slip(ilk, usr, int256(wad));
require(gem.transferFrom(msg.sender, address(this), wad), "GemJoin/failed-transfer");
emit Join(usr, wad);
}

/**
* 从系统中取出用户的 ERC-20 代币(减少 `msg.sender` 的 `vat` 余额,发送给 `usr`)
* @param usr 接收取出代币的用户地址(若为 `msg.sender` 自己,则 `usr = msg.sender`
* @param wad 取出的代币数量,单位为 wad (10^18)
* @notice
* - 此函数用于将用户的 ERC-20 代币从 MakerDAO 系统中取出,并将其发送到指定的用户地址。
* - 用户在取出代币时需要确保数量不超过 2^255,并且转账操作成功
* - 成功取出代币后,触发 `Exit` 事件
* - `vat[msg.sender]` -> `usr` (`msg.sender`在 vat 中的抵押品取出,取出给 `usr`)
*/
function exit(address usr, uint256 wad) external {
require(wad <= 2 ** 255, "GemJoin/overflow");
vat.slip(ilk, msg.sender, -int256(wad));
require(gem.transfer(usr, wad), "GemJoin/failed-transfer");
emit Exit(usr, wad);
}
}

/**
* @notice
* - 当用户想将 Dai 存入/取出 系统(`Vat`)时与该合约交互
*/
contract DaiJoin {
// --- Auth ---
mapping(address => uint256) public wards;

function rely(address usr) external auth {
wards[usr] = 1;
emit Rely(usr);
}

function deny(address usr) external auth {
wards[usr] = 0;
emit Deny(usr);
}

modifier auth() {
require(wards[msg.sender] == 1, "DaiJoin/not-authorized");
_;
}

VatLike public vat; // CDP Engine
DSTokenLike public dai; // Stablecoin Token
uint256 public live; // Active Flag

// Events
event Rely(address indexed usr);
event Deny(address indexed usr);
event Join(address indexed usr, uint256 wad);
event Exit(address indexed usr, uint256 wad);
event Cage();

constructor(address vat_, address dai_) public {
wards[msg.sender] = 1;
live = 1;
vat = VatLike(vat_);
dai = DSTokenLike(dai_);
}

/**
* @notice
* - 修改 DaiJoin 合约的状态,适配器排空(允许 `join`,不允许`exit`)
* - `join` 不受限:Dai 的存入不会直接影响系统的稳定性,因此不受限制。
* - `exit` 受限:防止在系统非活跃状态下生成新的 Dai,确保系统的稳定性和一致性。
*/
function cage() external auth {
live = 0;
emit Cage();
}

uint256 constant ONE = 10 ** 27;

function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x);
}

/**
* 将用户的 Dai 存入 Vat(减少 `msg.sender` 的 Dai,增加 `usr` 的 Dai 余额)
* @param usr 存入 Dai 的账户(若为自己,则 `usr = msg.sender`)
* @param wad 存入的 Dai 数量,单位为 wad (10^18)
*
* @notice
* - 此函数将 `msg.sender` 的 Dai 转移到 Vat 中,并增加 `usr` 的 Dai 余额。
* - `vat.move` 函数用于在 `Vat` 合约中移动 Dai 余额。
* - `dai.burn` 函数销毁 `msg.sender` 的 Dai。
* - 调用成功后,触发 `Join` 事件。
*
* @dev
* `Vat` 合约中的 Dai 不是以 ERC-20 代币的形式存在,而是作为系统内部的记账单位 `rad` (rad = 10^45)
* 所以将 Dai 存入 `Vat` 后,需要 `burn` `msg.sender` 相应数量的 Dai。
*/
function join(address usr, uint256 wad) external {
// vat.move(from, to, amount) amount 单位 [rad = 10^45] 10^27 * 10^18 = 10^45
// rad [rad = 10^45] ONE * wad = 10^18 * 10^27 = 10^45 == rad
// 增加 usr 在 Vat 合约中的 Dai 余额,减少 DaiJoin 合约在 Vat 中的 Dai 余额。
vat.move(address(this), usr, mul(ONE, wad));
// 从系统中实际销毁 msg.sender 的 Dai 代币,减少系统中的 Dai 供应。
// dai.burn(usr, wad)
dai.burn(msg.sender, wad);
emit Join(usr, wad);
}

/**
* 从系统中取出 Dai
* @param usr 接受 Dai 的地址(若为 `msg.sender` 自身,则 `usr = msg.sender`)
* @param wad 取出的 Dai 的数量
* @notice
* - 此函数将 `msg.sender` 存在系统`Vat`中的 Dai 取出,接收方为 `usr`
* - 因为 `Vat` 合约中的 Dai 不是以 ERC-20 代币的形式存在,所以取出 Dai
* 实际上是 `mint` 了新的 Dai,同时减少了 `Vat` 中 `msg.sender` 的数量
*/
function exit(address usr, uint256 wad) external {
require(live == 1, "DaiJoin/not-live");
// vat.move(from, to, amount) amount 单位 [rad = 10^45] 10^27 * 10^18 = 10^45
// rad [rad = 10^45] ONE * wad = 10^18 * 10^27 = 10^45 == rad
vat.move(msg.sender, address(this), mul(ONE, wad));
dai.mint(usr, wad);
emit Exit(usr, wad);
}
}