Q1ngying

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

0%

MakerDAO-Abacus

MakerDAO Abacus 合约分析

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

/// abaci.sol -- price decrease functions for auctions

// Copyright (C) 2020-2022 Dai Foundation
//
// 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;

interface Abacus {
// 1st arg: initial price [ray] 初始价格
// 2nd arg: seconds since auction start [seconds] 拍卖开始后的秒数
// returns: current auction price [ray] 当前拍卖价格
function price(uint256, uint256) external view returns (uint256);
}

/**
* @title 线性下降合约
* @author
* @notice 用于计算线性下降的价格
*/
contract LinearDecrease is Abacus {
// --- 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, "LinearDecrease/not-authorized");
_;
}

// --- Data ---
// 拍卖开始后价格归零后的秒数 [seconds]
uint256 public tau; // Seconds after auction start when the price reaches zero [seconds]

// --- Events ---
event Rely(address indexed usr);
event Deny(address indexed usr);

event File(bytes32 indexed what, uint256 data);

// --- Init ---
constructor() public {
wards[msg.sender] = 1;
emit Rely(msg.sender);
}

// --- Administration ---
/**
*
* @param what 要修改的数据,该函数 `what` 参数只能是 `tau`
* @param data 将 `tau` 修改为 `data`
*/
function file(bytes32 what, uint256 data) external auth {
if (what == "tau") tau = data;
else revert("LinearDecrease/file-unrecognized-param");
emit File(what, data);
}

// --- Math ---
uint256 constant RAY = 10 ** 27;

function add(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);
}

/// @dev 固定点乘法函数,将两个定点数相乘,并且将结果缩放回来。
/// @notice 该函数用于计算两个单位为 ray(10^27) 的数相乘
function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x * y;
require(y == 0 || z / y == x);
z = z / RAY;
}

// Price calculation when price is decreased linearly in proportion to time:
// tau: The number of seconds after the start of the auction where the price will hit 0
// top: Initial price
// dur: current seconds since the start of the auction
//
// Returns y = top * ((tau - dur) / tau)
//
// Note the internal call to mul multiples by RAY, thereby ensuring that the rmul calculation
// which utilizes top and tau (RAY values) is also a RAY value.
/**
* @dev 计算开始拍卖后对应秒数之后的价格
* @param top 初始价格
* @param dur 拍卖开始后的经历秒数
* @return `y = top * ((tau - dur) / tau)
* @notice RAY 内部调用了 mul 倍数,从而确保利用 top 和 tau(RAY 值)的 rmul 计算也是 RAY 值。
*/
function price(uint256 top, uint256 dur) external view override returns (uint256) {
// `tau` 拍卖开始后价格降至 0 所需的秒数 [seconds]
if (dur >= tau) return 0;
return rmul(top, mul(tau - dur, RAY) / tau);
}
}

/**
* @title 阶梯式指数下降合约
* @author
* @notice
* - 此合约返回有效价格
* - `cut` 和 `step` 值必须正确设置
*/
contract StairstepExponentialDecrease is Abacus {
// --- 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, "StairstepExponentialDecrease/not-authorized");
_;
}

// --- Data ---
uint256 public step; // Length of time between price drops [seconds] 价格下跌间隔时间
uint256 public cut; // Per-step multiplicative factor [ray] 每单位间隔时间的乘数因子 [ray]

// --- Events ---
event Rely(address indexed usr);
event Deny(address indexed usr);

event File(bytes32 indexed what, uint256 data);

// --- Init ---
// this contract to return a valid price 此合约返回有效价格
// @notice: `cut` and `step` values must be correctly set for `cut` 和 `step` 值必须正确设置
constructor() public {
wards[msg.sender] = 1;
emit Rely(msg.sender);
}

// --- Administration ---
/**
* @dev 访问权限控制函数,修改系统参数
* @param what 在本合约中,`what` 的参数有两个:`cut` 和 `step` ;若 `what == "cut"`,data 需要以 ray 为单位
* @param data 将对应参数修改为的值
*/
function file(bytes32 what, uint256 data) external auth {
if (what == "cut") require((cut = data) <= RAY, "StairstepExponentialDecrease/cut-gt-RAY");
else if (what == "step") step = data;
else revert("StairstepExponentialDecrease/file-unrecognized-param");
emit File(what, data);
}

// --- Math ---
uint256 constant RAY = 10 ** 27;

function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x * y;
require(y == 0 || z / y == x);
z = z / RAY;
}
// optimized version from dss PR #78

/**
* @dev 固定点数的幂运算
* @param x 底数
* @param n 指数
* @param b 基数,缩放结果
* @return z 幂运算的结果
*/
function rpow(uint256 x, uint256 n, uint256 b) internal pure returns (uint256 z) {
assembly {
switch n
case 0 { z := b }
default {
switch x
case 0 { z := 0 }
default {
switch mod(n, 2)
// 采取快速幂算法
case 0 { z := b }
default { z := x }
let half := div(b, 2) // for rounding.
for { n := div(n, 2) } n { n := div(n, 2) } {
let xx := mul(x, x)
if shr(128, x) { revert(0, 0) }
let xxRound := add(xx, half)
if lt(xxRound, xx) { revert(0, 0) }
x := div(xxRound, b)
if mod(n, 2) {
let zx := mul(z, x)
if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) }
let zxRound := add(zx, half)
if lt(zxRound, zx) { revert(0, 0) }
z := div(zxRound, b)
}
}
}
}
}
}

// top: initial price
// dur: seconds since the auction has started
// step: seconds between a price drop
// cut: cut encodes the percentage to decrease per step.
// For efficiency, the values is set as (1 - (% value / 100)) * RAY
// So, for a 1% decrease per step, cut would be (1 - 0.01) * RAY
//
// returns: top * (cut ^ dur)
//
//
/**
* @notice
* - 计算开始拍卖后对应秒数之后的价格
* - step:一次降低价格的间隔秒数
* - cut:每单位价格下降间隔秒数减少的百分比
* 为了提高效率,值设置为 (1 -(% value / 100)) * RAY
* 因此,每 step 减少 1%,cut 将是 (1- 0.01) * RAY
* @param top 初始价格
* @param dur 拍卖开始后经历的秒数
* @return `top * (cut ^ dur)`
*/
function price(uint256 top, uint256 dur) external view override returns (uint256) {
return rmul(top, rpow(cut, dur / step, RAY));
// top * cut * cut * cut …… (cut number = dur / strp)
}
}

// While an equivalent function can be obtained by setting step = 1 in StairstepExponentialDecrease,
// this continous (i.e. per-second) exponential decrease has be implemented as it is more gas-efficient
// than using the stairstep version with step = 1 (primarily due to 1 fewer SLOAD per price calculation).
/**
* @title 指数下降合约
* @author
* @notice
* - 虽然可以通过在 StairstepExponentialDecrease 中设置 `step = 1` 来获得等效函数,
* 但这种连续(即每秒)指数下降已被实现,因为它比使用 `step = 1` 的阶梯版本更节省 gas
* (主要是因为每次价格计算的 SLOAD 少 1 个)。
* - `cut` 和 `step` 值必须正确设置
* - 此合约返回有效价格
*/
contract ExponentialDecrease is Abacus {
// --- 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, "ExponentialDecrease/not-authorized");
_;
}

// --- Data ---
uint256 public cut; // Per-second multiplicative factor [ray] 每秒的乘数因子 [ray]

// --- Events ---
event Rely(address indexed usr);
event Deny(address indexed usr);

event File(bytes32 indexed what, uint256 data);

// --- Init ---
// @notice: `cut` value must be correctly set for `cut`的值必须正确设置
// this contract to return a valid price 此合约返回有效价格
constructor() public {
wards[msg.sender] = 1;
emit Rely(msg.sender);
}

// --- Administration ---
/**
* @dev 访问权限控制函数,修改系统参数
* @param what 在本合约中,`what` 的参数只能是`cut`
* @param data 将对应参数修改为的值,在本合约中,`data`必须以 ray 为单位
*/
function file(bytes32 what, uint256 data) external auth {
if (what == "cut") require((cut = data) <= RAY, "ExponentialDecrease/cut-gt-RAY");
else revert("ExponentialDecrease/file-unrecognized-param");
emit File(what, data);
}

// --- Math ---
uint256 constant RAY = 10 ** 27;

function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x * y;
require(y == 0 || z / y == x);
z = z / RAY;
}
// optimized version from dss PR #78

/**
* @dev 固定点数的幂运算
* @param x 底数
* @param n 指数
* @param b 基数,缩放结果
* @return z 幂运算的结果
*/
function rpow(uint256 x, uint256 n, uint256 b) internal pure returns (uint256 z) {
assembly {
switch n
case 0 { z := b }
default {
switch x
case 0 { z := 0 }
default {
switch mod(n, 2)
case 0 { z := b }
default { z := x }
let half := div(b, 2) // for rounding.
for { n := div(n, 2) } n { n := div(n, 2) } {
let xx := mul(x, x)
if shr(128, x) { revert(0, 0) }
let xxRound := add(xx, half)
if lt(xxRound, xx) { revert(0, 0) }
x := div(xxRound, b)
if mod(n, 2) {
let zx := mul(z, x)
if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) }
let zxRound := add(zx, half)
if lt(zxRound, zx) { revert(0, 0) }
z := div(zxRound, b)
}
}
}
}
}
}

// top: initial price
// dur: seconds since the auction has started
// cut: cut encodes the percentage to decrease per second.
// For efficiency, the values is set as (1 - (% value / 100)) * RAY
// So, for a 1% decrease per second, cut would be (1 - 0.01) * RAY
//
// returns: top * (cut ^ dur)
//
/**
* @notice
* - 计算开始拍卖后对应秒数之后的价格
* - cut:每秒数减少的百分比
* 为了提高效率,值设置为 (1 -(% value / 100)) * RAY
* 因此,每秒减少 1%,cut 将是 (1- 0.01) * RAY
* @param top 初始价格
* @param dur 拍卖开始后经历的秒数
* @return `top * (cut ^ dur)`
*/
function price(uint256 top, uint256 dur) external view override returns (uint256) {
return rmul(top, rpow(cut, dur, RAY));
}
}