@theqrl/qrl-contracts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,490 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // QRL Contracts (last updated v0.1.0) (utils/Strings.hyp)
3
+
4
+ pragma hyperion >=0.0;
5
+
6
+ import {Math} from "./math/Math.hyp";
7
+ import {SafeCast} from "./math/SafeCast.hyp";
8
+ import {SignedMath} from "./math/SignedMath.hyp";
9
+
10
+ /**
11
+ * @dev String operations.
12
+ */
13
+ library Strings {
14
+ using SafeCast for *;
15
+
16
+ bytes16 private constant HEX_DIGITS = "0123456789abcdef";
17
+ uint8 private constant ADDRESS_LENGTH = 20;
18
+ uint256 private constant SPECIAL_CHARS_LOOKUP =
19
+ (1 << 0x08) | // backspace
20
+ (1 << 0x09) | // tab
21
+ (1 << 0x0a) | // newline
22
+ (1 << 0x0c) | // form feed
23
+ (1 << 0x0d) | // carriage return
24
+ (1 << 0x22) | // double quote
25
+ (1 << 0x5c); // backslash
26
+
27
+ /**
28
+ * @dev The `value` string doesn't fit in the specified `length`.
29
+ */
30
+ error StringsInsufficientHexLength(uint256 value, uint256 length);
31
+
32
+ /**
33
+ * @dev The string being parsed contains characters that are not in scope of the given base.
34
+ */
35
+ error StringsInvalidChar();
36
+
37
+ /**
38
+ * @dev The string being parsed is not a properly formatted address.
39
+ */
40
+ error StringsInvalidAddressFormat();
41
+
42
+ /**
43
+ * @dev Converts a `uint256` to its ASCII `string` decimal representation.
44
+ */
45
+ function toString(uint256 value) internal pure returns (string memory) {
46
+ unchecked {
47
+ uint256 length = Math.log10(value) + 1;
48
+ string memory buffer = new string(length);
49
+ uint256 ptr;
50
+ assembly ("memory-safe") {
51
+ ptr := add(buffer, add(32, length))
52
+ }
53
+ while (true) {
54
+ ptr--;
55
+ assembly ("memory-safe") {
56
+ mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
57
+ }
58
+ value /= 10;
59
+ if (value == 0) break;
60
+ }
61
+ return buffer;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @dev Converts a `int256` to its ASCII `string` decimal representation.
67
+ */
68
+ function toStringSigned(int256 value) internal pure returns (string memory) {
69
+ return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
70
+ }
71
+
72
+ /**
73
+ * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
74
+ */
75
+ function toHexString(uint256 value) internal pure returns (string memory) {
76
+ unchecked {
77
+ return toHexString(value, Math.log256(value) + 1);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
83
+ */
84
+ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
85
+ uint256 localValue = value;
86
+ bytes memory buffer = new bytes(2 * length + 2);
87
+ buffer[0] = "0";
88
+ buffer[1] = "x";
89
+ for (uint256 i = 2 * length + 1; i > 1; --i) {
90
+ buffer[i] = HEX_DIGITS[localValue & 0xf];
91
+ localValue >>= 4;
92
+ }
93
+ if (localValue != 0) {
94
+ revert StringsInsufficientHexLength(value, length);
95
+ }
96
+ return string(buffer);
97
+ }
98
+
99
+ /**
100
+ * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
101
+ * representation.
102
+ */
103
+ function toHexString(address addr) internal pure returns (string memory) {
104
+ return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
105
+ }
106
+
107
+ /**
108
+ * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
109
+ * representation, according to EIP-55.
110
+ */
111
+ function toChecksumHexString(address addr) internal pure returns (string memory) {
112
+ bytes memory buffer = bytes(toHexString(addr));
113
+
114
+ // hash the hex part of buffer (skip length + 2 bytes, length 40)
115
+ uint256 hashValue;
116
+ assembly ("memory-safe") {
117
+ hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
118
+ }
119
+
120
+ for (uint256 i = 41; i > 1; --i) {
121
+ // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
122
+ if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
123
+ // case shift by xoring with 0x20
124
+ buffer[i] ^= 0x20;
125
+ }
126
+ hashValue >>= 4;
127
+ }
128
+ return string(buffer);
129
+ }
130
+
131
+ /**
132
+ * @dev Returns true if the two strings are equal.
133
+ */
134
+ function equal(string memory a, string memory b) internal pure returns (bool) {
135
+ return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
136
+ }
137
+
138
+ /**
139
+ * @dev Parse a decimal string and returns the value as a `uint256`.
140
+ *
141
+ * Requirements:
142
+ * - The string must be formatted as `[0-9]*`
143
+ * - The result must fit into an `uint256` type
144
+ */
145
+ function parseUint(string memory input) internal pure returns (uint256) {
146
+ return parseUint(input, 0, bytes(input).length);
147
+ }
148
+
149
+ /**
150
+ * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
151
+ * `end` (excluded).
152
+ *
153
+ * Requirements:
154
+ * - The substring must be formatted as `[0-9]*`
155
+ * - The result must fit into an `uint256` type
156
+ */
157
+ function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
158
+ (bool success, uint256 value) = tryParseUint(input, begin, end);
159
+ if (!success) revert StringsInvalidChar();
160
+ return value;
161
+ }
162
+
163
+ /**
164
+ * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
165
+ *
166
+ * NOTE: This function will revert if the result does not fit in a `uint256`.
167
+ */
168
+ function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
169
+ return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
170
+ }
171
+
172
+ /**
173
+ * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
174
+ * character.
175
+ *
176
+ * NOTE: This function will revert if the result does not fit in a `uint256`.
177
+ */
178
+ function tryParseUint(
179
+ string memory input,
180
+ uint256 begin,
181
+ uint256 end
182
+ ) internal pure returns (bool success, uint256 value) {
183
+ if (end > bytes(input).length || begin > end) return (false, 0);
184
+ return _tryParseUintUncheckedBounds(input, begin, end);
185
+ }
186
+
187
+ /**
188
+ * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
189
+ * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
190
+ */
191
+ function _tryParseUintUncheckedBounds(
192
+ string memory input,
193
+ uint256 begin,
194
+ uint256 end
195
+ ) private pure returns (bool success, uint256 value) {
196
+ bytes memory buffer = bytes(input);
197
+
198
+ uint256 result = 0;
199
+ for (uint256 i = begin; i < end; ++i) {
200
+ uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
201
+ if (chr > 9) return (false, 0);
202
+ result *= 10;
203
+ result += chr;
204
+ }
205
+ return (true, result);
206
+ }
207
+
208
+ /**
209
+ * @dev Parse a decimal string and returns the value as a `int256`.
210
+ *
211
+ * Requirements:
212
+ * - The string must be formatted as `[-+]?[0-9]*`
213
+ * - The result must fit in an `int256` type.
214
+ */
215
+ function parseInt(string memory input) internal pure returns (int256) {
216
+ return parseInt(input, 0, bytes(input).length);
217
+ }
218
+
219
+ /**
220
+ * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
221
+ * `end` (excluded).
222
+ *
223
+ * Requirements:
224
+ * - The substring must be formatted as `[-+]?[0-9]*`
225
+ * - The result must fit in an `int256` type.
226
+ */
227
+ function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
228
+ (bool success, int256 value) = tryParseInt(input, begin, end);
229
+ if (!success) revert StringsInvalidChar();
230
+ return value;
231
+ }
232
+
233
+ /**
234
+ * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
235
+ * the result does not fit in a `int256`.
236
+ *
237
+ * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
238
+ */
239
+ function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
240
+ return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
241
+ }
242
+
243
+ uint256 private constant ABS_MIN_INT256 = 2 ** 255;
244
+
245
+ /**
246
+ * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
247
+ * character or if the result does not fit in a `int256`.
248
+ *
249
+ * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
250
+ */
251
+ function tryParseInt(
252
+ string memory input,
253
+ uint256 begin,
254
+ uint256 end
255
+ ) internal pure returns (bool success, int256 value) {
256
+ if (end > bytes(input).length || begin > end) return (false, 0);
257
+ return _tryParseIntUncheckedBounds(input, begin, end);
258
+ }
259
+
260
+ /**
261
+ * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
262
+ * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
263
+ */
264
+ function _tryParseIntUncheckedBounds(
265
+ string memory input,
266
+ uint256 begin,
267
+ uint256 end
268
+ ) private pure returns (bool success, int256 value) {
269
+ bytes memory buffer = bytes(input);
270
+
271
+ // Check presence of a negative sign.
272
+ bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
273
+ bool positiveSign = sign == bytes1("+");
274
+ bool negativeSign = sign == bytes1("-");
275
+ uint256 offset = (positiveSign || negativeSign).toUint();
276
+
277
+ (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);
278
+
279
+ if (absSuccess && absValue < ABS_MIN_INT256) {
280
+ return (true, negativeSign ? -int256(absValue) : int256(absValue));
281
+ } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
282
+ return (true, type(int256).min);
283
+ } else return (false, 0);
284
+ }
285
+
286
+ /**
287
+ * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
288
+ *
289
+ * Requirements:
290
+ * - The string must be formatted as `(0x)?[0-9a-fA-F]*`
291
+ * - The result must fit in an `uint256` type.
292
+ */
293
+ function parseHexUint(string memory input) internal pure returns (uint256) {
294
+ return parseHexUint(input, 0, bytes(input).length);
295
+ }
296
+
297
+ /**
298
+ * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
299
+ * `end` (excluded).
300
+ *
301
+ * Requirements:
302
+ * - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
303
+ * - The result must fit in an `uint256` type.
304
+ */
305
+ function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
306
+ (bool success, uint256 value) = tryParseHexUint(input, begin, end);
307
+ if (!success) revert StringsInvalidChar();
308
+ return value;
309
+ }
310
+
311
+ /**
312
+ * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
313
+ *
314
+ * NOTE: This function will revert if the result does not fit in a `uint256`.
315
+ */
316
+ function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
317
+ return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
318
+ }
319
+
320
+ /**
321
+ * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
322
+ * invalid character.
323
+ *
324
+ * NOTE: This function will revert if the result does not fit in a `uint256`.
325
+ */
326
+ function tryParseHexUint(
327
+ string memory input,
328
+ uint256 begin,
329
+ uint256 end
330
+ ) internal pure returns (bool success, uint256 value) {
331
+ if (end > bytes(input).length || begin > end) return (false, 0);
332
+ return _tryParseHexUintUncheckedBounds(input, begin, end);
333
+ }
334
+
335
+ /**
336
+ * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
337
+ * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
338
+ */
339
+ function _tryParseHexUintUncheckedBounds(
340
+ string memory input,
341
+ uint256 begin,
342
+ uint256 end
343
+ ) private pure returns (bool success, uint256 value) {
344
+ bytes memory buffer = bytes(input);
345
+
346
+ // skip 0x prefix if present
347
+ bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
348
+ uint256 offset = hasPrefix.toUint() * 2;
349
+
350
+ uint256 result = 0;
351
+ for (uint256 i = begin + offset; i < end; ++i) {
352
+ uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
353
+ if (chr > 15) return (false, 0);
354
+ result *= 16;
355
+ unchecked {
356
+ // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
357
+ // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
358
+ result += chr;
359
+ }
360
+ }
361
+ return (true, result);
362
+ }
363
+
364
+ /**
365
+ * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
366
+ *
367
+ * Requirements:
368
+ * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
369
+ */
370
+ function parseAddress(string memory input) internal pure returns (address) {
371
+ return parseAddress(input, 0, bytes(input).length);
372
+ }
373
+
374
+ /**
375
+ * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
376
+ * `end` (excluded).
377
+ *
378
+ * Requirements:
379
+ * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
380
+ */
381
+ function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
382
+ (bool success, address value) = tryParseAddress(input, begin, end);
383
+ if (!success) revert StringsInvalidAddressFormat();
384
+ return value;
385
+ }
386
+
387
+ /**
388
+ * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
389
+ * formatted address. See {parseAddress-string} requirements.
390
+ */
391
+ function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
392
+ return tryParseAddress(input, 0, bytes(input).length);
393
+ }
394
+
395
+ /**
396
+ * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
397
+ * formatted address. See {parseAddress-string-uint256-uint256} requirements.
398
+ */
399
+ function tryParseAddress(
400
+ string memory input,
401
+ uint256 begin,
402
+ uint256 end
403
+ ) internal pure returns (bool success, address value) {
404
+ if (end > bytes(input).length || begin > end) return (false, address(0));
405
+
406
+ bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
407
+ uint256 expectedLength = 40 + hasPrefix.toUint() * 2;
408
+
409
+ // check that input is the correct length
410
+ if (end - begin == expectedLength) {
411
+ // length guarantees that this does not overflow, and value is at most type(uint160).max
412
+ (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
413
+ return (s, address(uint160(v)));
414
+ } else {
415
+ return (false, address(0));
416
+ }
417
+ }
418
+
419
+ function _tryParseChr(bytes1 chr) private pure returns (uint8) {
420
+ uint8 value = uint8(chr);
421
+
422
+ // Try to parse `chr`:
423
+ // - Case 1: [0-9]
424
+ // - Case 2: [a-f]
425
+ // - Case 3: [A-F]
426
+ // - otherwise not supported
427
+ unchecked {
428
+ if (value > 47 && value < 58) value -= 48;
429
+ else if (value > 96 && value < 103) value -= 87;
430
+ else if (value > 64 && value < 71) value -= 55;
431
+ else return type(uint8).max;
432
+ }
433
+
434
+ return value;
435
+ }
436
+
437
+ /**
438
+ * @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
439
+ *
440
+ * WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
441
+ *
442
+ * NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
443
+ * RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
444
+ * characters that are not in this range, but other tooling may provide different results.
445
+ */
446
+ function escapeJSON(string memory input) internal pure returns (string memory) {
447
+ bytes memory buffer = bytes(input);
448
+ bytes memory output = new bytes(2 * buffer.length); // worst case scenario
449
+ uint256 outputLength = 0;
450
+
451
+ for (uint256 i; i < buffer.length; ++i) {
452
+ bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
453
+ if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
454
+ output[outputLength++] = "\\";
455
+ if (char == 0x08) output[outputLength++] = "b";
456
+ else if (char == 0x09) output[outputLength++] = "t";
457
+ else if (char == 0x0a) output[outputLength++] = "n";
458
+ else if (char == 0x0c) output[outputLength++] = "f";
459
+ else if (char == 0x0d) output[outputLength++] = "r";
460
+ else if (char == 0x5c) output[outputLength++] = "\\";
461
+ else if (char == 0x22) {
462
+ // solhint-disable-next-line quotes
463
+ output[outputLength++] = '"';
464
+ }
465
+ } else {
466
+ output[outputLength++] = char;
467
+ }
468
+ }
469
+ // write the actual length and deallocate unused memory
470
+ assembly ("memory-safe") {
471
+ mstore(output, outputLength)
472
+ mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
473
+ }
474
+
475
+ return string(output);
476
+ }
477
+
478
+ /**
479
+ * @dev Reads a bytes32 from a bytes array without bounds checking.
480
+ *
481
+ * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
482
+ * assembly block as such would prevent some optimizations.
483
+ */
484
+ function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
485
+ // This is not memory safe in the general case, but all calls to this private function are within bounds.
486
+ assembly ("memory-safe") {
487
+ value := mload(add(buffer, add(0x20, offset)))
488
+ }
489
+ }
490
+ }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // QRL Contracts (last updated v0.1.0) (utils/introspection/IZRC165.hyp)
3
+
4
+ pragma hyperion >=0.0;
5
+
6
+ /**
7
+ * @dev Interface of the ZRC-165 standard, as defined in the
8
+ * https://eips.ethereum.org/EIPS/eip-165[ZRC].
9
+ *
10
+ * Implementers can declare support of contract interfaces, which can then be
11
+ * queried by others ({ZRC165Checker}).
12
+ *
13
+ * For an implementation, see {ZRC165}.
14
+ */
15
+ interface IZRC165 {
16
+ /**
17
+ * @dev Returns true if this contract implements the interface defined by
18
+ * `interfaceId`. See the corresponding
19
+ * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ZRC section]
20
+ * to learn more about how these ids are created.
21
+ *
22
+ * This function call must use less than 30 000 gas.
23
+ */
24
+ function supportsInterface(bytes4 interfaceId) external view returns (bool);
25
+ }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // QRL Contracts (last updated v0.1.0) (utils/introspection/ZRC165.hyp)
3
+
4
+ pragma hyperion >=0.0;
5
+
6
+ import {IZRC165} from "./IZRC165.hyp";
7
+
8
+ /**
9
+ * @dev Implementation of the {IZRC165} interface.
10
+ *
11
+ * Contracts that want to implement ZRC-165 should inherit from this contract and override {supportsInterface} to check
12
+ * for the additional interface id that will be supported. For example:
13
+ *
14
+ * ```hyperion
15
+ * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
16
+ * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
17
+ * }
18
+ * ```
19
+ */
20
+ abstract contract ZRC165 is IZRC165 {
21
+ /// @inheritdoc IZRC165
22
+ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
23
+ return interfaceId == type(IZRC165).interfaceId;
24
+ }
25
+ }