-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathUFixed18.sol
318 lines (285 loc) · 11.5 KB
/
UFixed18.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
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "../NumberMath.sol";
import "./Fixed18.sol";
import "./UFixed6.sol";
/// @dev UFixed18 type
type UFixed18 is uint256;
using UFixed18Lib for UFixed18 global;
type UFixed18Storage is bytes32;
using UFixed18StorageLib for UFixed18Storage global;
/**
* @title UFixed18Lib
* @notice Library for the unsigned fixed-decimal type.
*/
library UFixed18Lib {
error UFixed18UnderflowError(int256 value);
uint256 private constant BASE = 1e18;
UFixed18 public constant ZERO = UFixed18.wrap(0);
UFixed18 public constant ONE = UFixed18.wrap(BASE);
UFixed18 public constant MAX = UFixed18.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(Fixed18 a) internal pure returns (UFixed18) {
int256 value = Fixed18.unwrap(a);
if (value < 0) revert UFixed18UnderflowError(value);
return UFixed18.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 (UFixed18) {
return UFixed18.wrap(a * BASE);
}
/**
* @notice Creates a signed fixed-decimal from a base-6 signed fixed-decimal
* @param a Base-6 signed fixed-decimal
* @return New signed fixed-decimal
*/
function from(UFixed6 a) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed6.unwrap(a) * 1e12);
}
/**
* @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(UFixed18 a) internal pure returns (bool) {
return UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) + UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) - UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) * UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(NumberMath.divOut(UFixed18.unwrap(a) * UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) * BASE / UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(NumberMath.divOut(UFixed18.unwrap(a) * BASE, UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
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(UFixed18 a, uint256 b, uint256 c) internal pure returns (UFixed18) {
return muldiv(a, UFixed18.wrap(b), UFixed18.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(UFixed18 a, uint256 b, uint256 c) internal pure returns (UFixed18) {
return muldivOut(a, UFixed18.wrap(b), UFixed18.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(UFixed18 a, UFixed18 b, UFixed18 c) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) * UFixed18.unwrap(b) / UFixed18.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(UFixed18 a, UFixed18 b, UFixed18 c) internal pure returns (UFixed18) {
return UFixed18.wrap(NumberMath.divOut(UFixed18.unwrap(a) * UFixed18.unwrap(b), UFixed18.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(UFixed18 a, UFixed18 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(UFixed18 a, UFixed18 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(UFixed18 a, UFixed18 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(UFixed18 a, UFixed18 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(UFixed18 a, UFixed18 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(UFixed18 a, UFixed18 b) internal pure returns (uint256) {
(uint256 au, uint256 bu) = (UFixed18.unwrap(a), UFixed18.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 (UFixed18) {
return UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(Math.min(UFixed18.unwrap(a), UFixed18.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(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(Math.max(UFixed18.unwrap(a), UFixed18.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(UFixed18 a) internal pure returns (uint256) {
return UFixed18.unwrap(a) / BASE;
}
}
library UFixed18StorageLib {
function read(UFixed18Storage self) internal view returns (UFixed18 value) {
assembly ("memory-safe") {
value := sload(self)
}
}
function store(UFixed18Storage self, UFixed18 value) internal {
assembly ("memory-safe") {
sstore(self, value)
}
}
}