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,326 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Compact} from "./Compact.sol";
5
+ import {Test} from "forge-std/Test.sol";
6
+
7
+ /// @dev Wrapper contract to test reverts (vm.expectRevert needs external calls)
8
+ contract CompactWrapper {
9
+ function decode(
10
+ bytes memory data
11
+ ) external pure returns (uint256, uint256) {
12
+ return Compact.decode(data);
13
+ }
14
+
15
+ function decodeAt(
16
+ bytes memory data,
17
+ uint256 offset
18
+ ) external pure returns (uint256, uint256) {
19
+ return Compact.decodeAt(data, offset);
20
+ }
21
+ }
22
+
23
+ contract CompactTest is Test {
24
+ CompactWrapper wrapper;
25
+
26
+ function setUp() public {
27
+ wrapper = new CompactWrapper();
28
+ }
29
+
30
+ // ============ Single-byte mode (0-63) ============
31
+
32
+ function test_Encode_0() public pure {
33
+ assertEq(Compact.encode(0), hex"00");
34
+ }
35
+
36
+ function test_Encode_1() public pure {
37
+ assertEq(Compact.encode(1), hex"04");
38
+ }
39
+
40
+ function test_Encode_2() public pure {
41
+ assertEq(Compact.encode(2), hex"08");
42
+ }
43
+
44
+ function test_Encode_3() public pure {
45
+ assertEq(Compact.encode(3), hex"0c");
46
+ }
47
+
48
+ function test_Encode_63() public pure {
49
+ assertEq(Compact.encode(63), hex"fc");
50
+ }
51
+
52
+ // ============ Two-byte mode (64-16383) ============
53
+
54
+ function test_Encode_64() public pure {
55
+ assertEq(Compact.encode(64), hex"0101");
56
+ }
57
+
58
+ function test_Encode_65() public pure {
59
+ assertEq(Compact.encode(65), hex"0501");
60
+ }
61
+
62
+ function test_Encode_16383() public pure {
63
+ assertEq(Compact.encode(16383), hex"fdff");
64
+ }
65
+
66
+ // ============ Four-byte mode (16384-1073741823) ============
67
+
68
+ function test_Encode_16384() public pure {
69
+ assertEq(Compact.encode(16384), hex"02000100");
70
+ }
71
+
72
+ function test_Encode_1073741823() public pure {
73
+ assertEq(Compact.encode(1073741823), hex"feffffff");
74
+ }
75
+
76
+ // ============ Big-integer mode (>1073741823) ============
77
+
78
+ function test_Encode_1073741824() public pure {
79
+ assertEq(Compact.encode(1073741824), hex"0300000040");
80
+ }
81
+
82
+ function test_Encode_MaxUint32() public pure {
83
+ assertEq(Compact.encode(type(uint32).max), hex"03ffffffff");
84
+ }
85
+
86
+ function test_Encode_MaxUint64() public pure {
87
+ bytes memory encoded = Compact.encode(type(uint64).max);
88
+ assertEq(encoded[0], bytes1(0x13));
89
+ assertEq(encoded.length, 9);
90
+ }
91
+
92
+ function test_Encode_BigInt_100000000000000() public pure {
93
+ assertEq(Compact.encode(100000000000000), hex"0b00407a10f35a");
94
+ }
95
+
96
+ // ============ Decode single-byte ============
97
+
98
+ function test_Decode_0() public pure {
99
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"00");
100
+ assertEq(value, 0);
101
+ assertEq(bytesRead, 1);
102
+ }
103
+
104
+ function test_Decode_1() public pure {
105
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"04");
106
+ assertEq(value, 1);
107
+ assertEq(bytesRead, 1);
108
+ }
109
+
110
+ function test_Decode_63() public pure {
111
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"fc");
112
+ assertEq(value, 63);
113
+ assertEq(bytesRead, 1);
114
+ }
115
+
116
+ // ============ Decode two-byte ============
117
+
118
+ function test_Decode_64() public pure {
119
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"0101");
120
+ assertEq(value, 64);
121
+ assertEq(bytesRead, 2);
122
+ }
123
+
124
+ function test_Decode_65() public pure {
125
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"0501");
126
+ assertEq(value, 65);
127
+ assertEq(bytesRead, 2);
128
+ }
129
+
130
+ function test_Decode_16383() public pure {
131
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"fdff");
132
+ assertEq(value, 16383);
133
+ assertEq(bytesRead, 2);
134
+ }
135
+
136
+ // ============ Decode four-byte ============
137
+
138
+ function test_Decode_16384() public pure {
139
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"02000100");
140
+ assertEq(value, 16384);
141
+ assertEq(bytesRead, 4);
142
+ }
143
+
144
+ function test_Decode_1073741823() public pure {
145
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"feffffff");
146
+ assertEq(value, 1073741823);
147
+ assertEq(bytesRead, 4);
148
+ }
149
+
150
+ // ============ Decode big-integer ============
151
+
152
+ function test_Decode_1073741824() public pure {
153
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"0300000040");
154
+ assertEq(value, 1073741824);
155
+ assertEq(bytesRead, 5);
156
+ }
157
+
158
+ function test_Decode_MaxUint32() public pure {
159
+ (uint256 value, uint256 bytesRead) = Compact.decode(hex"03ffffffff");
160
+ assertEq(value, type(uint32).max);
161
+ assertEq(bytesRead, 5);
162
+ }
163
+
164
+ function test_Decode_BigInt_100000000000000() public pure {
165
+ (uint256 value, uint256 bytesRead) = Compact.decode(
166
+ hex"0b00407a10f35a"
167
+ );
168
+ assertEq(value, 100000000000000);
169
+ assertEq(bytesRead, 7);
170
+ }
171
+
172
+ // ============ DecodeAt with offset ============
173
+
174
+ function test_DecodeAt() public pure {
175
+ bytes memory data = hex"04010102000100";
176
+
177
+ (uint256 v1, uint256 r1) = Compact.decodeAt(data, 0);
178
+ assertEq(v1, 1);
179
+ assertEq(r1, 1);
180
+
181
+ (uint256 v2, uint256 r2) = Compact.decodeAt(data, 1);
182
+ assertEq(v2, 64);
183
+ assertEq(r2, 2);
184
+
185
+ (uint256 v3, uint256 r3) = Compact.decodeAt(data, 3);
186
+ assertEq(v3, 16384);
187
+ assertEq(r3, 4);
188
+ }
189
+
190
+ // ============ Typed encoding ============
191
+
192
+ function test_EncodeU8() public pure {
193
+ assertEq(Compact.encodeU8(42), hex"a8");
194
+ assertEq(Compact.encodeU8(255), hex"fd03");
195
+ }
196
+
197
+ function test_EncodeU16() public pure {
198
+ assertEq(Compact.encodeU16(1000), hex"a10f");
199
+ assertEq(Compact.encodeU16(65535), hex"feff0300");
200
+ }
201
+
202
+ function test_EncodeU32() public pure {
203
+ assertEq(Compact.encodeU32(1000000), hex"02093d00");
204
+ }
205
+
206
+ // ============ Typed decoding ============
207
+
208
+ function test_DecodeU8() public pure {
209
+ (uint8 value, uint256 bytesRead) = Compact.decodeU8(hex"a8");
210
+ assertEq(value, 42);
211
+ assertEq(bytesRead, 1);
212
+ }
213
+
214
+ function test_DecodeU16() public pure {
215
+ (uint16 value, uint256 bytesRead) = Compact.decodeU16(hex"a10f");
216
+ assertEq(value, 1000);
217
+ assertEq(bytesRead, 2);
218
+ }
219
+
220
+ function test_DecodeU32() public pure {
221
+ (uint32 value, uint256 bytesRead) = Compact.decodeU32(hex"02093d00");
222
+ assertEq(value, 1000000);
223
+ assertEq(bytesRead, 4);
224
+ }
225
+
226
+ // ============ encodedLength ============
227
+
228
+ function test_EncodedLength() public pure {
229
+ assertEq(Compact.encodedLength(0), 1);
230
+ assertEq(Compact.encodedLength(63), 1);
231
+ assertEq(Compact.encodedLength(64), 2);
232
+ assertEq(Compact.encodedLength(16383), 2);
233
+ assertEq(Compact.encodedLength(16384), 4);
234
+ assertEq(Compact.encodedLength(1073741823), 4);
235
+ assertEq(Compact.encodedLength(1073741824), 5);
236
+ assertEq(Compact.encodedLength(type(uint64).max), 9);
237
+ }
238
+
239
+ // ============ Mode checks ============
240
+
241
+ function test_IsSingleByte() public pure {
242
+ assertTrue(Compact.isSingleByte(0));
243
+ assertTrue(Compact.isSingleByte(63));
244
+ assertFalse(Compact.isSingleByte(64));
245
+ }
246
+
247
+ function test_IsTwoByte() public pure {
248
+ assertFalse(Compact.isTwoByte(63));
249
+ assertTrue(Compact.isTwoByte(64));
250
+ assertTrue(Compact.isTwoByte(16383));
251
+ assertFalse(Compact.isTwoByte(16384));
252
+ }
253
+
254
+ function test_IsFourByte() public pure {
255
+ assertFalse(Compact.isFourByte(16383));
256
+ assertTrue(Compact.isFourByte(16384));
257
+ assertTrue(Compact.isFourByte(1073741823));
258
+ assertFalse(Compact.isFourByte(1073741824));
259
+ }
260
+
261
+ function test_IsBigInt() public pure {
262
+ assertFalse(Compact.isBigInt(1073741823));
263
+ assertTrue(Compact.isBigInt(1073741824));
264
+ assertTrue(Compact.isBigInt(type(uint256).max));
265
+ }
266
+
267
+ // ============ Fuzz roundtrip ============
268
+
269
+ function testFuzz_Roundtrip(uint256 value) public pure {
270
+ bytes memory encoded = Compact.encode(value);
271
+ (uint256 decoded, ) = Compact.decode(encoded);
272
+ assertEq(decoded, value);
273
+ }
274
+
275
+ function testFuzz_RoundtripU8(uint8 value) public pure {
276
+ bytes memory encoded = Compact.encodeU8(value);
277
+ (uint8 decoded, ) = Compact.decodeU8(encoded);
278
+ assertEq(decoded, value);
279
+ }
280
+
281
+ function testFuzz_RoundtripU16(uint16 value) public pure {
282
+ bytes memory encoded = Compact.encodeU16(value);
283
+ (uint16 decoded, ) = Compact.decodeU16(encoded);
284
+ assertEq(decoded, value);
285
+ }
286
+
287
+ function testFuzz_RoundtripU32(uint32 value) public pure {
288
+ bytes memory encoded = Compact.encodeU32(value);
289
+ (uint32 decoded, ) = Compact.decodeU32(encoded);
290
+ assertEq(decoded, value);
291
+ }
292
+
293
+ function testFuzz_RoundtripU64(uint64 value) public pure {
294
+ bytes memory encoded = Compact.encodeU64(value);
295
+ (uint64 decoded, ) = Compact.decodeU64(encoded);
296
+ assertEq(decoded, value);
297
+ }
298
+
299
+ function testFuzz_RoundtripU128(uint128 value) public pure {
300
+ bytes memory encoded = Compact.encodeU128(value);
301
+ (uint128 decoded, ) = Compact.decodeU128(encoded);
302
+ assertEq(decoded, value);
303
+ }
304
+
305
+ // ============ Error cases (using wrapper for external calls) ============
306
+
307
+ function test_RevertOnEmptyData() public {
308
+ vm.expectRevert(Compact.InvalidCompactEncoding.selector);
309
+ wrapper.decode(hex"");
310
+ }
311
+
312
+ function test_RevertOnTruncatedTwoByte() public {
313
+ vm.expectRevert(Compact.InvalidCompactEncoding.selector);
314
+ wrapper.decode(hex"01"); // Mode 0b01 but only 1 byte
315
+ }
316
+
317
+ function test_RevertOnTruncatedFourByte() public {
318
+ vm.expectRevert(Compact.InvalidCompactEncoding.selector);
319
+ wrapper.decode(hex"020001"); // Mode 0b10 but only 3 bytes
320
+ }
321
+
322
+ function test_RevertOnTruncatedBigInt() public {
323
+ vm.expectRevert(Compact.InvalidCompactEncoding.selector);
324
+ wrapper.decode(hex"030000"); // Header says 4 bytes but only 2 provided
325
+ }
326
+ }
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Compact} from "./Compact/Compact.sol";
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { U128 } from "../Unsigned/U128.sol";
5
+
6
+ /// @title Scale Codec for the `int128` type.
7
+ /// @notice SCALE-compliant encoder/decoder for the `int128` type.
8
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
9
+ library I128 {
10
+ /// @notice Encodes an `int128` into SCALE format (16-byte two's-complement little-endian).
11
+ /// @param value The signed 128-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(int128 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `int128`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `int128`.
20
+ function decode(bytes memory data) internal pure returns (int128) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `int128` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `int128`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (int128 value) {
32
+ return int128(U128.decodeAt(data, offset));
33
+ }
34
+
35
+ /// @notice Converts an int128 to little-endian bytes16 (two's complement)
36
+ function toLittleEndian(int128 value) internal pure returns (bytes16 result) {
37
+ return U128.toLittleEndian(uint128(value));
38
+ }
39
+ }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { U16 } from "../Unsigned/U16.sol";
5
+
6
+ /// @title Scale Codec for the `int16` type.
7
+ /// @notice SCALE-compliant encoder/decoder for the `int16` type.
8
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
9
+ library I16 {
10
+ /// @notice Encodes an `int16` into SCALE format (2-byte two's-complement little-endian).
11
+ /// @param value The signed 16-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(int16 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `int16`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `int16`.
20
+ function decode(bytes memory data) internal pure returns (int16) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `int16` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `int16`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (int16 value) {
32
+ return int16(U16.decodeAt(data, offset));
33
+ }
34
+
35
+ /// @notice Converts an int16 to little-endian bytes2 (two's complement)
36
+ function toLittleEndian(int16 value) internal pure returns (bytes2 result) {
37
+ return U16.toLittleEndian(uint16(value));
38
+ }
39
+ }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { U256 } from "../Unsigned/U256.sol";
5
+
6
+ /// @title Scale Codec for the `int256` type.
7
+ /// @notice SCALE-compliant encoder/decoder for the `int256` type.
8
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
9
+ library I256 {
10
+ /// @notice Encodes an `int256` into SCALE format (32-byte two's-complement little-endian).
11
+ /// @param value The signed 256-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(int256 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `int256`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `int256`.
20
+ function decode(bytes memory data) internal pure returns (int256) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `int256` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `int256`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (int256 value) {
32
+ return int256(U256.decodeAt(data, offset));
33
+ }
34
+
35
+ /// @notice Converts an int256 to little-endian bytes32 (two's complement)
36
+ function toLittleEndian(int256 value) internal pure returns (bytes32 result) {
37
+ return U256.toLittleEndian(uint256(value));
38
+ }
39
+ }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { U32 } from "../Unsigned/U32.sol";
5
+
6
+ /// @title Scale Codec for the `int32` type.
7
+ /// @notice SCALE-compliant encoder/decoder for the `int32` type.
8
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
9
+ library I32 {
10
+ /// @notice Encodes an `int32` into SCALE format (4-byte two's-complement little-endian).
11
+ /// @param value The signed 32-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(int32 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `int32`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `int32`.
20
+ function decode(bytes memory data) internal pure returns (int32) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `int32` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `int32`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (int32 value) {
32
+ return int32(U32.decodeAt(data, offset));
33
+ }
34
+
35
+ /// @notice Converts an int32 to little-endian bytes4 (two's complement)
36
+ function toLittleEndian(int32 value) internal pure returns (bytes4 result) {
37
+ return U32.toLittleEndian(uint32(value));
38
+ }
39
+ }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { U64 } from "../Unsigned/U64.sol";
5
+
6
+ /// @title Scale Codec for the `int64` type.
7
+ /// @notice SCALE-compliant encoder/decoder for the `int64` type.
8
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
9
+ library I64 {
10
+ /// @notice Encodes an `int64` into SCALE format (8-byte two's-complement little-endian).
11
+ /// @param value The signed 64-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(int64 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `int64`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `int64`.
20
+ function decode(bytes memory data) internal pure returns (int64) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `int64` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `int64`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (int64 value) {
32
+ return int64(U64.decodeAt(data, offset));
33
+ }
34
+
35
+ /// @notice Converts an int64 to little-endian bytes8 (two's complement)
36
+ function toLittleEndian(int64 value) internal pure returns (bytes8 result) {
37
+ return U64.toLittleEndian(uint64(value));
38
+ }
39
+ }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { U8 } from "../Unsigned/U8.sol";
5
+
6
+ /// @title Scale Codec for the `int8` type.
7
+ /// @notice SCALE-compliant encoder/decoder for the `int8` type.
8
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
9
+ library I8 {
10
+ /// @notice Encodes an `int8` into SCALE format (1-byte two's-complement little-endian).
11
+ /// @param value The signed 8-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(int8 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `int8`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `int8`.
20
+ function decode(bytes memory data) internal pure returns (int8) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `int8` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `int8`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (int8 value) {
32
+ return int8(U8.decodeAt(data, offset));
33
+ }
34
+
35
+ /// @notice Converts an int8 to little-endian bytes1 (two's complement)
36
+ function toLittleEndian(int8 value) internal pure returns (bytes1 result) {
37
+ return U8.toLittleEndian(uint8(value));
38
+ }
39
+ }
@@ -0,0 +1,10 @@
1
+
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { I8 } from "./Signed/I8.sol";
6
+ import { I16 } from "./Signed/I16.sol";
7
+ import { I32 } from "./Signed/I32.sol";
8
+ import { I64 } from "./Signed/I64.sol";
9
+ import { I128 } from "./Signed/I128.sol";
10
+ import { I256 } from "./Signed/I256.sol";
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ /// @title Scale Codec for the `uint128` type.
5
+ /// @notice SCALE-compliant encoder/decoder for the `uint128` type.
6
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
7
+ library U128 {
8
+ error InvalidU128Length();
9
+
10
+ /// @notice Encodes an `uint128` into SCALE format (16-byte little-endian).
11
+ /// @param value The unsigned 128-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(uint128 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `uint128`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `uint128`.
20
+ function decode(bytes memory data) internal pure returns (uint128) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `uint128` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `uint128`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (uint128 value) {
32
+ if (data.length < offset + 16) revert InvalidU128Length();
33
+ assembly { let ptr := add(add(data, 32), offset) value := 0 for { let i := 0 } lt(i, 16) { i := add(i, 1) } { let b := and(mload(add(ptr, i)), 0xFF) value := or(value, shl(mul(i, 8), b)) } }
34
+ }
35
+
36
+ /// @notice Converts an `uint128` to little-endian bytes16
37
+ function toLittleEndian(uint128 value) internal pure returns (bytes16 result) {
38
+ assembly { let v := value v := or(shl(8, and(v, 0x00FF00FF00FF00FF00FF00FF00FF00FF)), shr(8, and(v, 0xFF00FF00FF00FF00FF00FF00FF00FF00))) v := or(shl(16, and(v, 0x0000FFFF0000FFFF0000FFFF0000FFFF)), shr(16, and(v, 0xFFFF0000FFFF0000FFFF0000FFFF0000))) v := or(shl(32, and(v, 0x00000000FFFFFFFF00000000FFFFFFFF)), shr(32, and(v, 0xFFFFFFFF00000000FFFFFFFF00000000))) v := or(shl(64, v), shr(64, v)) result := shl(128, v) }
39
+ }
40
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.20;
3
+
4
+ /// @title Scale Codec for the `uint16` type.
5
+ /// @notice SCALE-compliant encoder/decoder for the `uint16` type.
6
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
7
+ library U16 {
8
+ error InvalidU16Length();
9
+
10
+ /// @notice Encodes an `uint16` into SCALE format (2-byte little-endian).
11
+ /// @param value The unsigned 16-bit integer to encode.
12
+ /// @return SCALE-encoded byte sequence.
13
+ function encode(uint16 value) internal pure returns (bytes memory) {
14
+ return abi.encodePacked(toLittleEndian(value));
15
+ }
16
+
17
+ /// @notice Decodes SCALE-encoded bytes into an `uint16`.
18
+ /// @param data The SCALE-encoded byte sequence.
19
+ /// @return The decoded `uint16`.
20
+ function decode(bytes memory data) internal pure returns (uint16) {
21
+ return decodeAt(data, 0);
22
+ }
23
+
24
+ /// @notice Decodes an `uint16` at the specified offset.
25
+ /// @param data The SCALE-encoded byte sequence.
26
+ /// @param offset The byte offset to start decoding from.
27
+ /// @return value The decoded `uint16`.
28
+ function decodeAt(
29
+ bytes memory data,
30
+ uint256 offset
31
+ ) internal pure returns (uint16 value) {
32
+ if (data.length < offset + 2) revert InvalidU16Length();
33
+ assembly { let ptr := add(add(data, 32), offset) let b0 := and(mload(ptr), 0xFF) let b1 := and(mload(add(ptr, 1)), 0xFF) value := or(b0, shl(8, b1)) }
34
+ }
35
+
36
+ /// @notice Converts an `uint16` to little-endian bytes2
37
+ function toLittleEndian(uint16 value) internal pure returns (bytes2 result) {
38
+ return bytes2(uint16((value >> 8) | (value << 8)));
39
+ }
40
+ }