-
Notifications
You must be signed in to change notification settings - Fork 413
/
vat.sol
270 lines (236 loc) · 9.9 KB
/
vat.sol
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
// 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.5.12;
contract Vat {
// --- Auth ---
mapping (address => uint) public wards;
function rely(address usr) external note auth { require(live == 1, "Vat/not-live"); wards[usr] = 1; }
function deny(address usr) external note auth { require(live == 1, "Vat/not-live"); wards[usr] = 0; }
modifier auth {
require(wards[msg.sender] == 1, "Vat/not-authorized");
_;
}
mapping(address => mapping (address => uint)) public can;
function hope(address usr) external note { can[msg.sender][usr] = 1; }
function nope(address usr) external note { 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]
uint256 rate; // Accumulated Rates [ray]
uint256 spot; // Price with Safety Margin [ray]
uint256 line; // Debt Ceiling [rad]
uint256 dust; // Urn Debt Floor [rad]
}
struct Urn {
uint256 ink; // Locked Collateral [wad]
uint256 art; // Normalised Debt [wad]
}
mapping (bytes32 => Ilk) public ilks;
mapping (bytes32 => mapping (address => Urn )) public urns;
mapping (bytes32 => mapping (address => uint)) public gem; // [wad]
mapping (address => uint256) public dai; // [rad]
mapping (address => uint256) public sin; // [rad]
uint256 public debt; // Total Dai Issued [rad]
uint256 public vice; // Total Unbacked Dai [rad]
uint256 public Line; // Total Debt Ceiling [rad]
uint256 public live; // Active Flag
// --- Logs ---
event LogNote(
bytes4 indexed sig,
bytes32 indexed arg1,
bytes32 indexed arg2,
bytes32 indexed arg3,
bytes data
) anonymous;
modifier note {
_;
assembly {
// log an 'anonymous' event with a constant 6 words of calldata
// and four indexed topics: the selector and the first three args
let mark := msize() // end of memory ensures zero
mstore(0x40, add(mark, 288)) // update free memory pointer
mstore(mark, 0x20) // bytes type data offset
mstore(add(mark, 0x20), 224) // bytes size (padded)
calldatacopy(add(mark, 0x40), 0, 224) // bytes payload
log4(mark, 288, // calldata
shl(224, shr(224, calldataload(0))), // msg.sig
calldataload(4), // arg1
calldataload(36), // arg2
calldataload(68) // arg3
)
}
}
// --- Init ---
constructor() public {
wards[msg.sender] = 1;
live = 1;
}
// --- Math ---
function add(uint x, int y) internal pure returns (uint z) {
z = x + uint(y);
require(y >= 0 || z <= x);
require(y <= 0 || z >= x);
}
function sub(uint x, int y) internal pure returns (uint z) {
z = x - uint(y);
require(y <= 0 || z <= x);
require(y >= 0 || z >= x);
}
function mul(uint x, int y) internal pure returns (int z) {
z = int(x) * y;
require(int(x) >= 0);
require(y == 0 || z / y == int(x));
}
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x);
}
// --- Administration ---
function init(bytes32 ilk) external note auth {
require(ilks[ilk].rate == 0, "Vat/ilk-already-init");
ilks[ilk].rate = 10 ** 27;
}
function file(bytes32 what, uint data) external note auth {
require(live == 1, "Vat/not-live");
if (what == "Line") Line = data;
else revert("Vat/file-unrecognized-param");
}
function file(bytes32 ilk, bytes32 what, uint data) external note 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 note auth {
live = 0;
}
// --- Fungibility ---
function slip(bytes32 ilk, address usr, int256 wad) external note auth {
gem[ilk][usr] = add(gem[ilk][usr], wad);
}
function flux(bytes32 ilk, address src, address dst, uint256 wad) external note {
require(wish(src, msg.sender), "Vat/not-allowed");
gem[ilk][src] = sub(gem[ilk][src], wad);
gem[ilk][dst] = add(gem[ilk][dst], wad);
}
function move(address src, address dst, uint256 rad) external note {
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 ---
function frob(bytes32 i, address u, address v, address w, int dink, int dart) external note {
// 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);
int dtab = mul(ilk.rate, dart);
uint 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
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
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
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 ---
function fork(bytes32 ilk, address src, address dst, int dink, int dart) external note {
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);
uint utab = mul(u.art, i.rate);
uint 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 ---
function grab(bytes32 i, address u, address v, address w, int dink, int dart) external note 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);
int dtab = mul(ilk.rate, dart);
gem[i][v] = sub(gem[i][v], dink);
sin[w] = sub(sin[w], dtab);
vice = sub(vice, dtab);
}
// --- Settlement ---
function heal(uint rad) external note {
address u = msg.sender;
sin[u] = sub(sin[u], rad);
dai[u] = sub(dai[u], rad);
vice = sub(vice, rad);
debt = sub(debt, rad);
}
function suck(address u, address v, uint rad) external note auth {
sin[u] = add(sin[u], rad);
dai[v] = add(dai[v], rad);
vice = add(vice, rad);
debt = add(debt, rad);
}
// --- Rates ---
function fold(bytes32 i, address u, int rate) external note auth {
require(live == 1, "Vat/not-live");
Ilk storage ilk = ilks[i];
ilk.rate = add(ilk.rate, rate);
int rad = mul(ilk.Art, rate);
dai[u] = add(dai[u], rad);
debt = add(debt, rad);
}
}