-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathUFixed6.sol
328 lines (294 loc) · 11.9 KB
/
UFixed6.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
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
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "../NumberMath.sol";
import "./Fixed6.sol";
import "./UFixed18.sol";
/// @dev UFixed6 type
type UFixed6 is uint256;
using UFixed6Lib for UFixed6 global;
type UFixed6Storage is bytes32;
using UFixed6StorageLib for UFixed6Storage global;
/**
* @title UFixed6Lib
* @notice Library for the unsigned fixed-decimal type.
*/
library UFixed6Lib {
error UFixed6UnderflowError(int256 value);
uint256 private constant BASE = 1e6;
UFixed6 public constant ZERO = UFixed6.wrap(0);
UFixed6 public constant ONE = UFixed6.wrap(BASE);
UFixed6 public constant MAX = UFixed6.wrap(type(uint256).max);
/**
* @notice Creates a unsigned fixed-decimal from a signed fixed-decimal
* @param a Signed fixed-decimal
* @return New unsigned fixed-decimal
*/
function from(Fixed6 a) internal pure returns (UFixed6) {
int256 value = Fixed6.unwrap(a);
if (value < 0) revert UFixed6UnderflowError(value);
return UFixed6.wrap(uint256(value));
}
/**
* @notice Creates a unsigned fixed-decimal from a unsigned integer
* @param a Unsigned number
* @return New unsigned fixed-decimal
*/
function from(uint256 a) internal pure returns (UFixed6) {
return UFixed6.wrap(a * BASE);
}
/**
* @notice Creates an unsigned fixed-decimal from a base-18 unsigned fixed-decimal
* @param a Base-18 unsigned fixed-decimal
* @return New unsigned fixed-decimal
*/
function from(UFixed18 a) internal pure returns (UFixed6) {
return UFixed6.wrap(UFixed18.unwrap(a) / 1e12);
}
/**
* @notice Creates an unsigned fixed-decimal from a base-18 unsigned fixed-decimal
* @param a Base-18 unsigned fixed-decimal
* @param roundOut Whether to round the result away from zero if there is a remainder
* @return New unsigned fixed-decimal
*/
function from(UFixed18 a, bool roundOut) internal pure returns (UFixed6) {
return roundOut ? UFixed6.wrap(NumberMath.divOut(UFixed18.unwrap(a), 1e12)): from(a);
}
/**
* @notice Returns whether the unsigned fixed-decimal is equal to zero.
* @param a Unsigned fixed-decimal
* @return Whether the unsigned fixed-decimal is zero.
*/
function isZero(UFixed6 a) internal pure returns (bool) {
return UFixed6.unwrap(a) == 0;
}
/**
* @notice Adds two unsigned fixed-decimals `a` and `b` together
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Resulting summed unsigned fixed-decimal
*/
function add(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(UFixed6.unwrap(a) + UFixed6.unwrap(b));
}
/**
* @notice Subtracts unsigned fixed-decimal `b` from `a`
* @param a Unsigned fixed-decimal to subtract from
* @param b Unsigned fixed-decimal to subtract
* @return Resulting subtracted unsigned fixed-decimal
*/
function sub(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(UFixed6.unwrap(a) - UFixed6.unwrap(b));
}
/**
* @notice Multiplies two unsigned fixed-decimals `a` and `b` together
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Resulting multiplied unsigned fixed-decimal
*/
function mul(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(UFixed6.unwrap(a) * UFixed6.unwrap(b) / BASE);
}
/**
* @notice Multiplies two unsigned fixed-decimals `a` and `b` together, rounding the result up to the next integer if there is a remainder
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Resulting multiplied unsigned fixed-decimal
*/
function mulOut(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(NumberMath.divOut(UFixed6.unwrap(a) * UFixed6.unwrap(b), BASE));
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function div(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(UFixed6.unwrap(a) * BASE / UFixed6.unwrap(b));
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`, rounding the result up to the next integer if there is a remainder
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function divOut(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(NumberMath.divOut(UFixed6.unwrap(a) * BASE, UFixed6.unwrap(b)));
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`
* @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0` and `MAX` for `n/0`.
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function unsafeDiv(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
if (isZero(b)) {
return isZero(a) ? ONE : MAX;
} else {
return div(a, b);
}
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`, rounding the result up to the next integer if there is a remainder
* @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0` and `MAX` for `n/0`.
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function unsafeDivOut(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
if (isZero(b)) {
return isZero(a) ? ONE : MAX;
} else {
return divOut(a, b);
}
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First unsigned fixed-decimal
* @param b Unsigned number to multiply by
* @param c Unsigned number to divide by
* @return Resulting computation
*/
function muldiv(UFixed6 a, uint256 b, uint256 c) internal pure returns (UFixed6) {
return muldiv(a, UFixed6.wrap(b), UFixed6.wrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion, rounding the result up to the next integer if there is a remainder
* @param a First unsigned fixed-decimal
* @param b Unsigned number to multiply by
* @param c Unsigned number to divide by
* @return Resulting computation
*/
function muldivOut(UFixed6 a, uint256 b, uint256 c) internal pure returns (UFixed6) {
return muldivOut(a, UFixed6.wrap(b), UFixed6.wrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First unsigned fixed-decimal
* @param b Unsigned fixed-decimal to multiply by
* @param c Unsigned fixed-decimal to divide by
* @return Resulting computation
*/
function muldiv(UFixed6 a, UFixed6 b, UFixed6 c) internal pure returns (UFixed6) {
return UFixed6.wrap(UFixed6.unwrap(a) * UFixed6.unwrap(b) / UFixed6.unwrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion, rounding the result up to the next integer if there is a remainder
* @param a First unsigned fixed-decimal
* @param b Unsigned fixed-decimal to multiply by
* @param c Unsigned fixed-decimal to divide by
* @return Resulting computation
*/
function muldivOut(UFixed6 a, UFixed6 b, UFixed6 c) internal pure returns (UFixed6) {
return UFixed6.wrap(NumberMath.divOut(UFixed6.unwrap(a) * UFixed6.unwrap(b), UFixed6.unwrap(c)));
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is equal to `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is equal to `b`
*/
function eq(UFixed6 a, UFixed6 b) internal pure returns (bool) {
return compare(a, b) == 1;
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is greater than `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is greater than `b`
*/
function gt(UFixed6 a, UFixed6 b) internal pure returns (bool) {
return compare(a, b) == 2;
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is less than `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is less than `b`
*/
function lt(UFixed6 a, UFixed6 b) internal pure returns (bool) {
return compare(a, b) == 0;
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is greater than or equal to `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is greater than or equal to `b`
*/
function gte(UFixed6 a, UFixed6 b) internal pure returns (bool) {
return gt(a, b) || eq(a, b);
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is less than or equal to `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is less than or equal to `b`
*/
function lte(UFixed6 a, UFixed6 b) internal pure returns (bool) {
return lt(a, b) || eq(a, b);
}
/**
* @notice Compares the unsigned fixed-decimals `a` and `b`
* @dev Returns: 2 for greater than
* 1 for equal to
* 0 for less than
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Compare result of `a` and `b`
*/
function compare(UFixed6 a, UFixed6 b) internal pure returns (uint256) {
(uint256 au, uint256 bu) = (UFixed6.unwrap(a), UFixed6.unwrap(b));
if (au > bu) return 2;
if (au < bu) return 0;
return 1;
}
/**
* @notice Returns a unsigned fixed-decimal representing the ratio of `a` over `b`
* @param a First unsigned number
* @param b Second unsigned number
* @return Ratio of `a` over `b`
*/
function ratio(uint256 a, uint256 b) internal pure returns (UFixed6) {
return UFixed6.wrap(a * BASE / b);
}
/**
* @notice Returns the minimum of unsigned fixed-decimals `a` and `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Minimum of `a` and `b`
*/
function min(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(Math.min(UFixed6.unwrap(a), UFixed6.unwrap(b)));
}
/**
* @notice Returns the maximum of unsigned fixed-decimals `a` and `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Maximum of `a` and `b`
*/
function max(UFixed6 a, UFixed6 b) internal pure returns (UFixed6) {
return UFixed6.wrap(Math.max(UFixed6.unwrap(a), UFixed6.unwrap(b)));
}
/**
* @notice Converts the unsigned fixed-decimal into an integer, truncating any decimal portion
* @param a Unsigned fixed-decimal
* @return Truncated unsigned number
*/
function truncate(UFixed6 a) internal pure returns (uint256) {
return UFixed6.unwrap(a) / BASE;
}
}
library UFixed6StorageLib {
function read(UFixed6Storage self) internal view returns (UFixed6 value) {
assembly ("memory-safe") {
value := sload(self)
}
}
function store(UFixed6Storage self, UFixed6 value) internal {
assembly ("memory-safe") {
sstore(self, value)
}
}
}