solidity-scale-codec 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.
Files changed (38) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +48 -0
  3. package/package.json +41 -0
  4. package/src/Scale/Array/BoolArr.sol +44 -0
  5. package/src/Scale/Array/I128Arr.sol +44 -0
  6. package/src/Scale/Array/I16Arr.sol +44 -0
  7. package/src/Scale/Array/I256Arr.sol +44 -0
  8. package/src/Scale/Array/I32Arr.sol +44 -0
  9. package/src/Scale/Array/I64Arr.sol +44 -0
  10. package/src/Scale/Array/I8Arr.sol +44 -0
  11. package/src/Scale/Array/U128Arr.sol +44 -0
  12. package/src/Scale/Array/U16Arr.sol +44 -0
  13. package/src/Scale/Array/U256Arr.sol +44 -0
  14. package/src/Scale/Array/U32Arr.sol +44 -0
  15. package/src/Scale/Array/U64Arr.sol +44 -0
  16. package/src/Scale/Array/U8Arr.sol +44 -0
  17. package/src/Scale/Array.sol +17 -0
  18. package/src/Scale/Bool/Bool.sol +40 -0
  19. package/src/Scale/Bool.sol +4 -0
  20. package/src/Scale/Compact/Compact.sol +543 -0
  21. package/src/Scale/Compact/Compact.t.sol +326 -0
  22. package/src/Scale/Compact.sol +4 -0
  23. package/src/Scale/Signed/I128.sol +39 -0
  24. package/src/Scale/Signed/I16.sol +39 -0
  25. package/src/Scale/Signed/I256.sol +39 -0
  26. package/src/Scale/Signed/I32.sol +39 -0
  27. package/src/Scale/Signed/I64.sol +39 -0
  28. package/src/Scale/Signed/I8.sol +39 -0
  29. package/src/Scale/Signed.sol +10 -0
  30. package/src/Scale/Unsigned/U128.sol +40 -0
  31. package/src/Scale/Unsigned/U16.sol +40 -0
  32. package/src/Scale/Unsigned/U256.sol +40 -0
  33. package/src/Scale/Unsigned/U32.sol +40 -0
  34. package/src/Scale/Unsigned/U64.sol +40 -0
  35. package/src/Scale/Unsigned/U8.sol +40 -0
  36. package/src/Scale/Unsigned.sol +10 -0
  37. package/src/Utils/LittleEndian/LittleEndian.sol +354 -0
  38. package/src/Utils/LittleEndian/LittleEndian.t.sol +542 -0
@@ -0,0 +1,543 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ /// @title Compact
5
+ /// @notice SCALE Compact encoding for space-efficient unsigned integer representation
6
+ /// @dev Encoding modes based on value range:
7
+ /// - 0b00: single-byte mode (0-63), 6 bits of data
8
+ /// - 0b01: two-byte mode (64-16383), 14 bits of data
9
+ /// - 0b10: four-byte mode (16384-1073741823), 30 bits of data
10
+ /// - 0b11: big-integer mode (>1073741823), variable length up to 67 bytes
11
+ /// @dev Reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding/#compact-general-integers
12
+ library Compact {
13
+ error InvalidCompactEncoding();
14
+ error ValueOutOfRange();
15
+ error NonCanonicalEncoding();
16
+
17
+ // ============ Constants ============
18
+
19
+ uint256 private constant SINGLE_BYTE_MAX = 0x3F; // 63
20
+ uint256 private constant TWO_BYTE_MAX = 0x3FFF; // 16383
21
+ uint256 private constant FOUR_BYTE_MAX = 0x3FFFFFFF; // 1073741823
22
+
23
+ uint8 private constant MODE_SINGLE = 0x00;
24
+ uint8 private constant MODE_TWO = 0x01;
25
+ uint8 private constant MODE_FOUR = 0x02;
26
+ uint8 private constant MODE_BIG = 0x03;
27
+
28
+ // ============ Encoding (generic) ============
29
+
30
+ /// @notice Encodes a uint256 value to SCALE Compact format
31
+ /// @param value The value to encode
32
+ /// @return Compact-encoded bytes in little-endian
33
+ function encode(uint256 value) internal pure returns (bytes memory) {
34
+ if (value <= SINGLE_BYTE_MAX) {
35
+ return _encodeSingleByte(value);
36
+ } else if (value <= TWO_BYTE_MAX) {
37
+ return _encodeTwoByte(value);
38
+ } else if (value <= FOUR_BYTE_MAX) {
39
+ return _encodeFourByte(value);
40
+ } else {
41
+ return _encodeBigInt(value);
42
+ }
43
+ }
44
+
45
+ // ============ Encoding (typed) ============
46
+
47
+ /// @notice Encodes uint8 to Compact
48
+ function encodeU8(uint8 value) internal pure returns (bytes memory) {
49
+ if (value <= SINGLE_BYTE_MAX) {
50
+ return _encodeSingleByte(value);
51
+ } else {
52
+ return _encodeTwoByte(value);
53
+ }
54
+ }
55
+
56
+ /// @notice Encodes uint16 to Compact
57
+ function encodeU16(uint16 value) internal pure returns (bytes memory) {
58
+ if (value <= SINGLE_BYTE_MAX) {
59
+ return _encodeSingleByte(value);
60
+ } else if (value <= TWO_BYTE_MAX) {
61
+ return _encodeTwoByte(value);
62
+ } else {
63
+ return _encodeFourByte(value);
64
+ }
65
+ }
66
+
67
+ /// @notice Encodes uint32 to Compact
68
+ function encodeU32(uint32 value) internal pure returns (bytes memory) {
69
+ if (value <= SINGLE_BYTE_MAX) {
70
+ return _encodeSingleByte(value);
71
+ } else if (value <= TWO_BYTE_MAX) {
72
+ return _encodeTwoByte(value);
73
+ } else if (value <= FOUR_BYTE_MAX) {
74
+ return _encodeFourByte(value);
75
+ } else {
76
+ return _encodeBigInt(uint256(value));
77
+ }
78
+ }
79
+
80
+ /// @notice Encodes uint64 to Compact
81
+ function encodeU64(uint64 value) internal pure returns (bytes memory) {
82
+ return encode(uint256(value));
83
+ }
84
+
85
+ /// @notice Encodes uint128 to Compact
86
+ function encodeU128(uint128 value) internal pure returns (bytes memory) {
87
+ return encode(uint256(value));
88
+ }
89
+
90
+ // ============ Internal encoding helpers ============
91
+
92
+ /// @dev Single-byte mode: value << 2 | 0b00
93
+ function _encodeSingleByte(
94
+ uint256 value
95
+ ) private pure returns (bytes memory) {
96
+ return abi.encodePacked(uint8(value << 2));
97
+ }
98
+
99
+ /// @dev Two-byte mode: value << 2 | 0b01, little-endian
100
+ function _encodeTwoByte(uint256 value) private pure returns (bytes memory) {
101
+ uint16 encoded = uint16((value << 2) | MODE_TWO);
102
+ return abi.encodePacked(uint8(encoded & 0xFF), uint8(encoded >> 8));
103
+ }
104
+
105
+ /// @dev Four-byte mode: value << 2 | 0b10, little-endian
106
+ function _encodeFourByte(
107
+ uint256 value
108
+ ) private pure returns (bytes memory) {
109
+ uint32 encoded = uint32((value << 2) | MODE_FOUR);
110
+ return
111
+ abi.encodePacked(
112
+ uint8(encoded & 0xFF),
113
+ uint8((encoded >> 8) & 0xFF),
114
+ uint8((encoded >> 16) & 0xFF),
115
+ uint8(encoded >> 24)
116
+ );
117
+ }
118
+
119
+ /// @dev Big-integer mode: header byte + raw value bytes
120
+ /// Header: ((bytesNeeded - 4) << 2) | 0b11
121
+ function _encodeBigInt(uint256 value) private pure returns (bytes memory) {
122
+ uint8 bytesNeeded = _bytesNeeded(value);
123
+ uint8 header = ((bytesNeeded - 4) << 2) | MODE_BIG;
124
+
125
+ bytes memory result = new bytes(1 + bytesNeeded);
126
+ result[0] = bytes1(header);
127
+
128
+ uint256 v = value;
129
+ for (uint8 i = 0; i < bytesNeeded; i++) {
130
+ result[1 + i] = bytes1(uint8(v & 0xFF));
131
+ v >>= 8;
132
+ }
133
+
134
+ return result;
135
+ }
136
+
137
+ /// @dev Calculate minimum bytes needed to represent value
138
+ function _bytesNeeded(uint256 value) private pure returns (uint8) {
139
+ if (value == 0) return 1;
140
+
141
+ uint8 count = 0;
142
+ while (value > 0) {
143
+ value >>= 8;
144
+ count++;
145
+ }
146
+ return count;
147
+ }
148
+
149
+ // ============ Decoding ============
150
+
151
+ /// @notice Decodes Compact bytes to uint256
152
+ /// @param data The compact-encoded bytes
153
+ /// @return value The decoded value
154
+ /// @return bytesRead Number of bytes consumed
155
+ function decode(
156
+ bytes memory data
157
+ ) internal pure returns (uint256 value, uint256 bytesRead) {
158
+ return decodeAt(data, 0);
159
+ }
160
+
161
+ /// @notice Decodes Compact bytes starting at offset
162
+ /// @dev Validates canonical encoding (rejects values that could use smaller mode)
163
+ /// @param data The compact-encoded bytes
164
+ /// @param offset Starting position in data
165
+ /// @return value The decoded value
166
+ /// @return bytesRead Number of bytes consumed
167
+ function decodeAt(
168
+ bytes memory data,
169
+ uint256 offset
170
+ ) internal pure returns (uint256 value, uint256 bytesRead) {
171
+ if (data.length <= offset) revert InvalidCompactEncoding();
172
+
173
+ uint8 firstByte = uint8(data[offset]);
174
+ uint8 mode = firstByte & 0x03;
175
+
176
+ if (mode == MODE_SINGLE) {
177
+ // Single-byte mode: valid range 0-63
178
+ value = firstByte >> 2;
179
+ bytesRead = 1;
180
+ } else if (mode == MODE_TWO) {
181
+ // Two-byte mode: valid range 64-16383
182
+ if (data.length < offset + 2) revert InvalidCompactEncoding();
183
+ value =
184
+ (uint256(firstByte) |
185
+ (uint256(uint8(data[offset + 1])) << 8)) >>
186
+ 2;
187
+
188
+ // Canonical check: must be > SINGLE_BYTE_MAX
189
+ if (value <= SINGLE_BYTE_MAX) revert NonCanonicalEncoding();
190
+
191
+ bytesRead = 2;
192
+ } else if (mode == MODE_FOUR) {
193
+ // Four-byte mode: valid range 16384-1073741823
194
+ if (data.length < offset + 4) revert InvalidCompactEncoding();
195
+ value =
196
+ (uint256(firstByte) |
197
+ (uint256(uint8(data[offset + 1])) << 8) |
198
+ (uint256(uint8(data[offset + 2])) << 16) |
199
+ (uint256(uint8(data[offset + 3])) << 24)) >>
200
+ 2;
201
+
202
+ // Canonical check: must be > TWO_BYTE_MAX
203
+ if (value <= TWO_BYTE_MAX) revert NonCanonicalEncoding();
204
+
205
+ bytesRead = 4;
206
+ } else {
207
+ // Big-integer mode: valid range > 1073741823
208
+ uint8 byteLen = (firstByte >> 2) + 4;
209
+ if (data.length < offset + 1 + byteLen)
210
+ revert InvalidCompactEncoding();
211
+
212
+ value = 0;
213
+ for (uint8 i = 0; i < byteLen; i++) {
214
+ value |= uint256(uint8(data[offset + 1 + i])) << (i * 8);
215
+ }
216
+
217
+ // Canonical check: must be > FOUR_BYTE_MAX
218
+ if (value <= FOUR_BYTE_MAX) revert NonCanonicalEncoding();
219
+
220
+ // Additional canonical check: value must require exactly byteLen bytes
221
+ // (no leading zero bytes allowed)
222
+ uint8 minBytesNeeded = _bytesNeeded(value);
223
+ if (byteLen != minBytesNeeded) revert NonCanonicalEncoding();
224
+
225
+ bytesRead = 1 + byteLen;
226
+ }
227
+ }
228
+
229
+ // ============ Typed decoding with range validation ============
230
+
231
+ /// @notice Decodes to uint8 with range validation
232
+ function decodeU8(
233
+ bytes memory data
234
+ ) internal pure returns (uint8 value, uint256 bytesRead) {
235
+ return decodeU8At(data, 0);
236
+ }
237
+
238
+ /// @notice Decodes to uint8 at offset with range validation
239
+ function decodeU8At(
240
+ bytes memory data,
241
+ uint256 offset
242
+ ) internal pure returns (uint8 value, uint256 bytesRead) {
243
+ if (data.length <= offset) revert InvalidCompactEncoding();
244
+
245
+ uint8 firstByte = uint8(data[offset]);
246
+ uint8 mode = firstByte & 0x03;
247
+
248
+ // u8 only supports single-byte and two-byte modes
249
+ if (mode == MODE_SINGLE) {
250
+ value = uint8(firstByte >> 2);
251
+ bytesRead = 1;
252
+ } else if (mode == MODE_TWO) {
253
+ if (data.length < offset + 2) revert InvalidCompactEncoding();
254
+ uint256 v = (uint256(firstByte) |
255
+ (uint256(uint8(data[offset + 1])) << 8)) >> 2;
256
+
257
+ // Canonical: must be > 63 and <= 255 for u8
258
+ if (v <= SINGLE_BYTE_MAX) revert NonCanonicalEncoding();
259
+ if (v > type(uint8).max) revert ValueOutOfRange();
260
+
261
+ value = uint8(v);
262
+ bytesRead = 2;
263
+ } else {
264
+ revert ValueOutOfRange();
265
+ }
266
+ }
267
+
268
+ /// @notice Decodes to uint16 with range validation
269
+ function decodeU16(
270
+ bytes memory data
271
+ ) internal pure returns (uint16 value, uint256 bytesRead) {
272
+ return decodeU16At(data, 0);
273
+ }
274
+
275
+ /// @notice Decodes to uint16 at offset with range validation
276
+ function decodeU16At(
277
+ bytes memory data,
278
+ uint256 offset
279
+ ) internal pure returns (uint16 value, uint256 bytesRead) {
280
+ if (data.length <= offset) revert InvalidCompactEncoding();
281
+
282
+ uint8 firstByte = uint8(data[offset]);
283
+ uint8 mode = firstByte & 0x03;
284
+
285
+ if (mode == MODE_SINGLE) {
286
+ value = uint16(firstByte >> 2);
287
+ bytesRead = 1;
288
+ } else if (mode == MODE_TWO) {
289
+ if (data.length < offset + 2) revert InvalidCompactEncoding();
290
+ uint256 v = (uint256(firstByte) |
291
+ (uint256(uint8(data[offset + 1])) << 8)) >> 2;
292
+
293
+ if (v <= SINGLE_BYTE_MAX) revert NonCanonicalEncoding();
294
+
295
+ value = uint16(v);
296
+ bytesRead = 2;
297
+ } else if (mode == MODE_FOUR) {
298
+ if (data.length < offset + 4) revert InvalidCompactEncoding();
299
+ uint256 v = (uint256(firstByte) |
300
+ (uint256(uint8(data[offset + 1])) << 8) |
301
+ (uint256(uint8(data[offset + 2])) << 16) |
302
+ (uint256(uint8(data[offset + 3])) << 24)) >> 2;
303
+
304
+ if (v <= TWO_BYTE_MAX) revert NonCanonicalEncoding();
305
+ if (v > type(uint16).max) revert ValueOutOfRange();
306
+
307
+ value = uint16(v);
308
+ bytesRead = 4;
309
+ } else {
310
+ revert ValueOutOfRange();
311
+ }
312
+ }
313
+
314
+ /// @notice Decodes to uint32 with range validation
315
+ function decodeU32(
316
+ bytes memory data
317
+ ) internal pure returns (uint32 value, uint256 bytesRead) {
318
+ return decodeU32At(data, 0);
319
+ }
320
+
321
+ /// @notice Decodes to uint32 at offset with range validation
322
+ function decodeU32At(
323
+ bytes memory data,
324
+ uint256 offset
325
+ ) internal pure returns (uint32 value, uint256 bytesRead) {
326
+ if (data.length <= offset) revert InvalidCompactEncoding();
327
+
328
+ uint8 firstByte = uint8(data[offset]);
329
+ uint8 mode = firstByte & 0x03;
330
+
331
+ if (mode == MODE_SINGLE) {
332
+ value = uint32(firstByte >> 2);
333
+ bytesRead = 1;
334
+ } else if (mode == MODE_TWO) {
335
+ if (data.length < offset + 2) revert InvalidCompactEncoding();
336
+ uint256 v = (uint256(firstByte) |
337
+ (uint256(uint8(data[offset + 1])) << 8)) >> 2;
338
+
339
+ if (v <= SINGLE_BYTE_MAX) revert NonCanonicalEncoding();
340
+
341
+ value = uint32(v);
342
+ bytesRead = 2;
343
+ } else if (mode == MODE_FOUR) {
344
+ if (data.length < offset + 4) revert InvalidCompactEncoding();
345
+ uint256 v = (uint256(firstByte) |
346
+ (uint256(uint8(data[offset + 1])) << 8) |
347
+ (uint256(uint8(data[offset + 2])) << 16) |
348
+ (uint256(uint8(data[offset + 3])) << 24)) >> 2;
349
+
350
+ if (v <= TWO_BYTE_MAX) revert NonCanonicalEncoding();
351
+
352
+ value = uint32(v);
353
+ bytesRead = 4;
354
+ } else {
355
+ // Big-integer mode
356
+ uint8 byteLen = (firstByte >> 2) + 4;
357
+
358
+ // For u32, only byteLen == 4 is valid (header 0b11 means 4 extra bytes)
359
+ if (byteLen != 4) revert ValueOutOfRange();
360
+
361
+ if (data.length < offset + 5) revert InvalidCompactEncoding();
362
+
363
+ uint256 v = 0;
364
+ for (uint8 i = 0; i < 4; i++) {
365
+ v |= uint256(uint8(data[offset + 1 + i])) << (i * 8);
366
+ }
367
+
368
+ if (v <= FOUR_BYTE_MAX) revert NonCanonicalEncoding();
369
+ if (v > type(uint32).max) revert ValueOutOfRange();
370
+
371
+ value = uint32(v);
372
+ bytesRead = 5;
373
+ }
374
+ }
375
+
376
+ /// @notice Decodes to uint64 with range validation
377
+ function decodeU64(
378
+ bytes memory data
379
+ ) internal pure returns (uint64 value, uint256 bytesRead) {
380
+ return decodeU64At(data, 0);
381
+ }
382
+
383
+ /// @notice Decodes to uint64 at offset with range validation
384
+ function decodeU64At(
385
+ bytes memory data,
386
+ uint256 offset
387
+ ) internal pure returns (uint64 value, uint256 bytesRead) {
388
+ if (data.length <= offset) revert InvalidCompactEncoding();
389
+
390
+ uint8 firstByte = uint8(data[offset]);
391
+ uint8 mode = firstByte & 0x03;
392
+
393
+ if (mode == MODE_SINGLE) {
394
+ value = uint64(firstByte >> 2);
395
+ bytesRead = 1;
396
+ } else if (mode == MODE_TWO) {
397
+ if (data.length < offset + 2) revert InvalidCompactEncoding();
398
+ uint256 v = (uint256(firstByte) |
399
+ (uint256(uint8(data[offset + 1])) << 8)) >> 2;
400
+
401
+ if (v <= SINGLE_BYTE_MAX) revert NonCanonicalEncoding();
402
+
403
+ value = uint64(v);
404
+ bytesRead = 2;
405
+ } else if (mode == MODE_FOUR) {
406
+ if (data.length < offset + 4) revert InvalidCompactEncoding();
407
+ uint256 v = (uint256(firstByte) |
408
+ (uint256(uint8(data[offset + 1])) << 8) |
409
+ (uint256(uint8(data[offset + 2])) << 16) |
410
+ (uint256(uint8(data[offset + 3])) << 24)) >> 2;
411
+
412
+ if (v <= TWO_BYTE_MAX) revert NonCanonicalEncoding();
413
+
414
+ value = uint64(v);
415
+ bytesRead = 4;
416
+ } else {
417
+ // Big-integer mode
418
+ uint8 byteLen = (firstByte >> 2) + 4;
419
+
420
+ // For u64, max byteLen is 8
421
+ if (byteLen > 8) revert ValueOutOfRange();
422
+
423
+ if (data.length < offset + 1 + byteLen)
424
+ revert InvalidCompactEncoding();
425
+
426
+ uint256 v = 0;
427
+ for (uint8 i = 0; i < byteLen; i++) {
428
+ v |= uint256(uint8(data[offset + 1 + i])) << (i * 8);
429
+ }
430
+
431
+ if (v <= FOUR_BYTE_MAX) revert NonCanonicalEncoding();
432
+
433
+ // Canonical check: no leading zero bytes
434
+ uint8 minBytes = _bytesNeeded(v);
435
+ if (byteLen != minBytes) revert NonCanonicalEncoding();
436
+
437
+ if (v > type(uint64).max) revert ValueOutOfRange();
438
+
439
+ value = uint64(v);
440
+ bytesRead = 1 + byteLen;
441
+ }
442
+ }
443
+
444
+ /// @notice Decodes to uint128 with range validation
445
+ function decodeU128(
446
+ bytes memory data
447
+ ) internal pure returns (uint128 value, uint256 bytesRead) {
448
+ return decodeU128At(data, 0);
449
+ }
450
+
451
+ /// @notice Decodes to uint128 at offset with range validation
452
+ function decodeU128At(
453
+ bytes memory data,
454
+ uint256 offset
455
+ ) internal pure returns (uint128 value, uint256 bytesRead) {
456
+ if (data.length <= offset) revert InvalidCompactEncoding();
457
+
458
+ uint8 firstByte = uint8(data[offset]);
459
+ uint8 mode = firstByte & 0x03;
460
+
461
+ if (mode == MODE_SINGLE) {
462
+ value = uint128(firstByte >> 2);
463
+ bytesRead = 1;
464
+ } else if (mode == MODE_TWO) {
465
+ if (data.length < offset + 2) revert InvalidCompactEncoding();
466
+ uint256 v = (uint256(firstByte) |
467
+ (uint256(uint8(data[offset + 1])) << 8)) >> 2;
468
+
469
+ if (v <= SINGLE_BYTE_MAX) revert NonCanonicalEncoding();
470
+
471
+ value = uint128(v);
472
+ bytesRead = 2;
473
+ } else if (mode == MODE_FOUR) {
474
+ if (data.length < offset + 4) revert InvalidCompactEncoding();
475
+ uint256 v = (uint256(firstByte) |
476
+ (uint256(uint8(data[offset + 1])) << 8) |
477
+ (uint256(uint8(data[offset + 2])) << 16) |
478
+ (uint256(uint8(data[offset + 3])) << 24)) >> 2;
479
+
480
+ if (v <= TWO_BYTE_MAX) revert NonCanonicalEncoding();
481
+
482
+ value = uint128(v);
483
+ bytesRead = 4;
484
+ } else {
485
+ // Big-integer mode
486
+ uint8 byteLen = (firstByte >> 2) + 4;
487
+
488
+ // For u128, max byteLen is 16
489
+ if (byteLen > 16) revert ValueOutOfRange();
490
+
491
+ if (data.length < offset + 1 + byteLen)
492
+ revert InvalidCompactEncoding();
493
+
494
+ uint256 v = 0;
495
+ for (uint8 i = 0; i < byteLen; i++) {
496
+ v |= uint256(uint8(data[offset + 1 + i])) << (i * 8);
497
+ }
498
+
499
+ if (v <= FOUR_BYTE_MAX) revert NonCanonicalEncoding();
500
+
501
+ // Canonical check: no leading zero bytes
502
+ uint8 minBytes = _bytesNeeded(v);
503
+ if (byteLen != minBytes) revert NonCanonicalEncoding();
504
+
505
+ if (v > type(uint128).max) revert ValueOutOfRange();
506
+
507
+ value = uint128(v);
508
+ bytesRead = 1 + byteLen;
509
+ }
510
+ }
511
+
512
+ // ============ Utilities ============
513
+
514
+ /// @notice Returns the encoded length without allocating
515
+ /// @param value The value to check
516
+ /// @return The number of bytes needed for compact encoding
517
+ function encodedLength(uint256 value) internal pure returns (uint256) {
518
+ if (value <= SINGLE_BYTE_MAX) return 1;
519
+ if (value <= TWO_BYTE_MAX) return 2;
520
+ if (value <= FOUR_BYTE_MAX) return 4;
521
+ return 1 + _bytesNeeded(value);
522
+ }
523
+
524
+ /// @notice Checks if a value fits in single-byte mode
525
+ function isSingleByte(uint256 value) internal pure returns (bool) {
526
+ return value <= SINGLE_BYTE_MAX;
527
+ }
528
+
529
+ /// @notice Checks if a value fits in two-byte mode
530
+ function isTwoByte(uint256 value) internal pure returns (bool) {
531
+ return value > SINGLE_BYTE_MAX && value <= TWO_BYTE_MAX;
532
+ }
533
+
534
+ /// @notice Checks if a value fits in four-byte mode
535
+ function isFourByte(uint256 value) internal pure returns (bool) {
536
+ return value > TWO_BYTE_MAX && value <= FOUR_BYTE_MAX;
537
+ }
538
+
539
+ /// @notice Checks if a value requires big-integer mode
540
+ function isBigInt(uint256 value) internal pure returns (bool) {
541
+ return value > FOUR_BYTE_MAX;
542
+ }
543
+ }