solidity-scale-codec 0.1.3 → 0.3.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 (104) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/DEFINITIONS.md +132 -0
  3. package/README.md +97 -1
  4. package/package.json +16 -4
  5. package/src/LittleEndian/LittleEndianU128.sol +4 -2
  6. package/src/LittleEndian/LittleEndianU16.sol +2 -2
  7. package/src/LittleEndian/LittleEndianU256.sol +4 -2
  8. package/src/LittleEndian/LittleEndianU32.sol +4 -2
  9. package/src/LittleEndian/LittleEndianU64.sol +4 -2
  10. package/src/LittleEndian/LittleEndianU8.sol +2 -2
  11. package/src/Scale/Address/Address.sol +55 -0
  12. package/src/Scale/Address.sol +4 -0
  13. package/src/Scale/Array/BoolArr.sol +16 -2
  14. package/src/Scale/Array/I128Arr.sol +16 -2
  15. package/src/Scale/Array/I16Arr.sol +16 -2
  16. package/src/Scale/Array/I256Arr.sol +16 -2
  17. package/src/Scale/Array/I32Arr.sol +16 -2
  18. package/src/Scale/Array/I64Arr.sol +16 -2
  19. package/src/Scale/Array/I8Arr.sol +16 -2
  20. package/src/Scale/Array/U128Arr.sol +16 -2
  21. package/src/Scale/Array/U16Arr.sol +16 -2
  22. package/src/Scale/Array/U256Arr.sol +16 -2
  23. package/src/Scale/Array/U32Arr.sol +16 -2
  24. package/src/Scale/Array/U64Arr.sol +16 -2
  25. package/src/Scale/Array/U8Arr.sol +16 -2
  26. package/src/Scale/Bool/Bool.sol +12 -0
  27. package/src/Scale/Bytes/Bytes.sol +63 -0
  28. package/src/Scale/Bytes/Bytes16.sol +53 -0
  29. package/src/Scale/Bytes/Bytes2.sol +53 -0
  30. package/src/Scale/Bytes/Bytes32.sol +53 -0
  31. package/src/Scale/Bytes/Bytes4.sol +53 -0
  32. package/src/Scale/Bytes/Bytes8.sol +53 -0
  33. package/src/Scale/Bytes.sol +8 -0
  34. package/src/Scale/Compact/Compact.sol +37 -3
  35. package/src/Scale/Signed/I128.sol +11 -0
  36. package/src/Scale/Signed/I16.sol +11 -0
  37. package/src/Scale/Signed/I256.sol +11 -0
  38. package/src/Scale/Signed/I32.sol +11 -0
  39. package/src/Scale/Signed/I64.sol +11 -0
  40. package/src/Scale/Signed/I8.sol +11 -0
  41. package/src/Scale/Unsigned/U128.sol +16 -2
  42. package/src/Scale/Unsigned/U16.sol +16 -2
  43. package/src/Scale/Unsigned/U256.sol +16 -2
  44. package/src/Scale/Unsigned/U32.sol +16 -2
  45. package/src/Scale/Unsigned/U64.sol +16 -2
  46. package/src/Scale/Unsigned/U8.sol +16 -2
  47. package/src/Xcm/Types/Version.sol +5 -0
  48. package/src/Xcm/VersionedXcm/VersionedXcm.sol +27 -0
  49. package/src/Xcm/VersionedXcm/VersionedXcmCodec.sol +70 -0
  50. package/src/Xcm/v3/Constants.sol +5 -0
  51. package/src/Xcm/v3/MaybeErrorCode/MaybeErrorCode.sol +73 -0
  52. package/src/Xcm/v3/MaybeErrorCode/MaybeErrorCodeCodec.sol +94 -0
  53. package/src/Xcm/v5/Asset/Asset.sol +13 -0
  54. package/src/Xcm/v5/Asset/AssetCodec.sol +72 -0
  55. package/src/Xcm/v5/AssetFilter/AssetFilter.sol +61 -0
  56. package/src/Xcm/v5/AssetFilter/AssetFilterCodec.sol +105 -0
  57. package/src/Xcm/v5/AssetId/AssetId.sol +10 -0
  58. package/src/Xcm/v5/AssetId/AssetIdCodec.sol +57 -0
  59. package/src/Xcm/v5/AssetInstance/AssetInstance.sol +139 -0
  60. package/src/Xcm/v5/AssetInstance/AssetInstanceCodec.sol +174 -0
  61. package/src/Xcm/v5/AssetTransferFilter/AssetTransferFilter.sol +82 -0
  62. package/src/Xcm/v5/AssetTransferFilter/AssetTransferFilterCodec.sol +95 -0
  63. package/src/Xcm/v5/Assets/Assets.sol +14 -0
  64. package/src/Xcm/v5/Assets/AssetsCodec.sol +94 -0
  65. package/src/Xcm/v5/BodyId/BodyId.sol +121 -0
  66. package/src/Xcm/v5/BodyId/BodyIdCodec.sol +128 -0
  67. package/src/Xcm/v5/BodyPart/BodyPart.sol +105 -0
  68. package/src/Xcm/v5/BodyPart/BodyPartCodec.sol +134 -0
  69. package/src/Xcm/v5/Constants.sol +15 -0
  70. package/src/Xcm/v5/Fungibility/Fungibility.sol +60 -0
  71. package/src/Xcm/v5/Fungibility/FungibilityCodec.sol +128 -0
  72. package/src/Xcm/v5/Hint/Hint.sol +40 -0
  73. package/src/Xcm/v5/Hint/HintCodec.sol +82 -0
  74. package/src/Xcm/v5/Instruction/Instruction.sol +1217 -0
  75. package/src/Xcm/v5/Instruction/InstructionCodec.sol +321 -0
  76. package/src/Xcm/v5/Junction/Junction.sol +258 -0
  77. package/src/Xcm/v5/Junction/JunctionCodec.sol +329 -0
  78. package/src/Xcm/v5/Junctions/Junctions.sol +12 -0
  79. package/src/Xcm/v5/Junctions/JunctionsCodec.sol +120 -0
  80. package/src/Xcm/v5/Location/Location.sol +12 -0
  81. package/src/Xcm/v5/Location/LocationCodec.sol +68 -0
  82. package/src/Xcm/v5/NetworkId/NetworkId.sol +115 -0
  83. package/src/Xcm/v5/NetworkId/NetworkIdCodec.sol +114 -0
  84. package/src/Xcm/v5/OriginKind/OriginKind.sol +14 -0
  85. package/src/Xcm/v5/OriginKind/OriginKindCodec.sol +66 -0
  86. package/src/Xcm/v5/PalletInfo/PalletInfo.sol +18 -0
  87. package/src/Xcm/v5/PalletInfo/PalletInfoCodec.sol +119 -0
  88. package/src/Xcm/v5/QueryResponseInfo/QueryResponseInfo.sol +16 -0
  89. package/src/Xcm/v5/QueryResponseInfo/QueryResponseInfoCodec.sol +91 -0
  90. package/src/Xcm/v5/Response/Response.sol +145 -0
  91. package/src/Xcm/v5/Response/ResponseCodec.sol +174 -0
  92. package/src/Xcm/v5/Types/QueryId.sol +5 -0
  93. package/src/Xcm/v5/Weight/Weight.sol +10 -0
  94. package/src/Xcm/v5/Weight/WeightCodec.sol +87 -0
  95. package/src/Xcm/v5/WeightLimit/WeightLimit.sol +48 -0
  96. package/src/Xcm/v5/WeightLimit/WeightLimitCodec.sol +88 -0
  97. package/src/Xcm/v5/WildAsset/WildAsset.sol +112 -0
  98. package/src/Xcm/v5/WildAsset/WildAssetCodec.sol +166 -0
  99. package/src/Xcm/v5/WildFungibility/WildFungibility.sol +10 -0
  100. package/src/Xcm/v5/WildFungibility/WildFungibilityCodec.sol +74 -0
  101. package/src/Xcm/v5/Xcm/Xcm.sol +27 -0
  102. package/src/Xcm/v5/Xcm/XcmCodec.sol +83 -0
  103. package/src/Xcm/v5/XcmError/XcmError.sol +122 -0
  104. package/src/Xcm/v5/XcmError/XcmErrorCodec.sol +85 -0
@@ -0,0 +1,174 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Compact} from "../../../Scale/Compact.sol";
5
+ import {Bytes4} from "../../../Scale/Bytes/Bytes4.sol";
6
+ import {Bytes8} from "../../../Scale/Bytes/Bytes8.sol";
7
+ import {Bytes16} from "../../../Scale/Bytes/Bytes16.sol";
8
+ import {Bytes32} from "../../../Scale/Bytes/Bytes32.sol";
9
+ import {AssetInstance, AssetInstanceType} from "./AssetInstance.sol";
10
+
11
+ /// @title SCALE Codec for XCM v5 `AssetInstance`
12
+ /// @notice SCALE-compliant encoder/decoder for the `AssetInstance` type.
13
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
14
+ /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/index.html
15
+ library AssetInstanceCodec {
16
+ error InvalidAssetInstanceLength();
17
+ error InvalidAssetInstanceType(uint8 iType);
18
+ error InvalidAssetInstancePayload();
19
+
20
+ /// @notice Encodes an `AssetInstance` struct into bytes.
21
+ /// @param assetInstance The `AssetInstance` struct to encode.
22
+ /// @return SCALE-encoded byte sequence representing the `AssetInstance`.
23
+ function encode(
24
+ AssetInstance memory assetInstance
25
+ ) internal pure returns (bytes memory) {
26
+ return abi.encodePacked(assetInstance.iType, assetInstance.payload);
27
+ }
28
+
29
+ /// @notice Returns the total number of bytes that an `AssetInstance` would occupy when encoded, based on the type and payload.
30
+ /// @param data The byte sequence containing the encoded `AssetInstance`.
31
+ /// @param offset The starting index in `data` from which to calculate the encoded size.
32
+ /// @return The total number of bytes that the `AssetInstance` occupies in its encoded form, including the type byte and payload.
33
+ function encodedSizeAt(
34
+ bytes memory data,
35
+ uint256 offset
36
+ ) internal pure returns (uint256) {
37
+ if (data.length < offset + 1) {
38
+ revert InvalidAssetInstanceLength();
39
+ }
40
+ uint8 iType = uint8(data[offset]);
41
+ uint256 payloadLength;
42
+ if (iType == uint8(AssetInstanceType.Index)) {
43
+ payloadLength = Compact.encodedSizeAt(data, offset + 1);
44
+ } else if (iType == uint8(AssetInstanceType.Array4)) {
45
+ payloadLength = 4;
46
+ } else if (iType == uint8(AssetInstanceType.Array8)) {
47
+ payloadLength = 8;
48
+ } else if (iType == uint8(AssetInstanceType.Array16)) {
49
+ payloadLength = 16;
50
+ } else if (iType == uint8(AssetInstanceType.Array32)) {
51
+ payloadLength = 32;
52
+ } else if (iType == uint8(AssetInstanceType.Undefined)) {
53
+ payloadLength = 0;
54
+ } else {
55
+ revert InvalidAssetInstanceType(iType);
56
+ }
57
+
58
+ if (data.length < offset + 1 + payloadLength) {
59
+ revert InvalidAssetInstanceLength();
60
+ }
61
+
62
+ return 1 + payloadLength;
63
+ }
64
+
65
+ /// @notice Decodes an `AssetInstance` struct from bytes starting at the beginning of the data.
66
+ /// @param data The byte sequence containing the encoded `AssetInstance`.
67
+ /// @return assetInstance The decoded `AssetInstance` struct.
68
+ /// @return bytesRead The total number of bytes read from `data` to decode the `AssetInstance`.
69
+ function decode(
70
+ bytes memory data
71
+ )
72
+ internal
73
+ pure
74
+ returns (AssetInstance memory assetInstance, uint256 bytesRead)
75
+ {
76
+ return decodeAt(data, 0);
77
+ }
78
+
79
+ /// @notice Decodes an `AssetInstance` struct from bytes starting at a given offset.
80
+ /// @param data The byte sequence containing the encoded `AssetInstance`.
81
+ /// @param offset The starting index in `data` from which to decode the `AssetInstance`.
82
+ /// @return assetInstance The decoded `AssetInstance` struct.
83
+ /// @return bytesRead The total number of bytes read from `data` to decode the `AssetInstance`.
84
+ function decodeAt(
85
+ bytes memory data,
86
+ uint256 offset
87
+ )
88
+ internal
89
+ pure
90
+ returns (AssetInstance memory assetInstance, uint256 bytesRead)
91
+ {
92
+ if (data.length < offset + 1) {
93
+ revert InvalidAssetInstanceLength();
94
+ }
95
+ uint8 iType = uint8(data[offset]);
96
+ uint256 payloadLength = encodedSizeAt(data, offset) - 1; // subtract 1 byte for the iType
97
+ bytes memory payload = new bytes(payloadLength);
98
+ for (uint256 i = 0; i < payloadLength; i++) {
99
+ payload[i] = data[offset + 1 + i];
100
+ }
101
+
102
+ assetInstance = AssetInstance({
103
+ iType: AssetInstanceType(iType),
104
+ payload: payload
105
+ });
106
+ bytesRead = 1 + payloadLength;
107
+ }
108
+
109
+ /// @notice Extracts the index value from an `Index` asset instance. Reverts if the asset instance is not of type `Index` or if the decoded index exceeds the maximum value for `uint128`.
110
+ /// @param assetInstance The `AssetInstance` struct to decode, which must have type `Index`.
111
+ /// @return idx The index value extracted from the asset instance.
112
+ function asIndex(
113
+ AssetInstance memory assetInstance
114
+ ) internal pure returns (uint128 idx) {
115
+ if (assetInstance.iType != AssetInstanceType.Index) {
116
+ revert InvalidAssetInstanceType(uint8(assetInstance.iType));
117
+ }
118
+ (uint256 decodedIndex, ) = Compact.decode(assetInstance.payload);
119
+ if (decodedIndex > type(uint128).max) {
120
+ revert InvalidAssetInstancePayload();
121
+ }
122
+ unchecked {
123
+ idx = uint128(decodedIndex);
124
+ }
125
+ }
126
+
127
+ /// @notice Extracts the 4-byte data from an `Array4` asset instance. Reverts if the asset instance is not of type `Array4`.
128
+ /// @param assetInstance The `AssetInstance` struct to decode, which must have type `Array4`.
129
+ /// @return data The 4-byte data extracted from the asset instance.
130
+ function asArray4(
131
+ AssetInstance memory assetInstance
132
+ ) internal pure returns (bytes4 data) {
133
+ if (assetInstance.iType != AssetInstanceType.Array4) {
134
+ revert InvalidAssetInstanceType(uint8(assetInstance.iType));
135
+ }
136
+ return Bytes4.decode(assetInstance.payload);
137
+ }
138
+
139
+ /// @notice Extracts the 8-byte data from an `Array8` asset instance. Reverts if the asset instance is not of type `Array8`.
140
+ /// @param assetInstance The `AssetInstance` struct to decode, which must have type `Array8`.
141
+ /// @return data The 8-byte data extracted from the asset instance.
142
+ function asArray8(
143
+ AssetInstance memory assetInstance
144
+ ) internal pure returns (bytes8 data) {
145
+ if (assetInstance.iType != AssetInstanceType.Array8) {
146
+ revert InvalidAssetInstanceType(uint8(assetInstance.iType));
147
+ }
148
+ return Bytes8.decode(assetInstance.payload);
149
+ }
150
+
151
+ /// @notice Extracts the 16-byte data from an `Array16` asset instance. Reverts if the asset instance is not of type `Array16`.
152
+ /// @param assetInstance The `AssetInstance` struct to decode, which must have type `Array16`.
153
+ /// @return data The 16-byte data extracted from the asset instance.
154
+ function asArray16(
155
+ AssetInstance memory assetInstance
156
+ ) internal pure returns (bytes16 data) {
157
+ if (assetInstance.iType != AssetInstanceType.Array16) {
158
+ revert InvalidAssetInstanceType(uint8(assetInstance.iType));
159
+ }
160
+ return Bytes16.decode(assetInstance.payload);
161
+ }
162
+
163
+ /// @notice Extracts the 32-byte data from an `Array32` asset instance. Reverts if the asset instance is not of type `Array32`.
164
+ /// @param assetInstance The `AssetInstance` struct to decode, which must have type `Array32`.
165
+ /// @return data The 32-byte data extracted from the asset instance.
166
+ function asArray32(
167
+ AssetInstance memory assetInstance
168
+ ) internal pure returns (bytes32 data) {
169
+ if (assetInstance.iType != AssetInstanceType.Array32) {
170
+ revert InvalidAssetInstanceType(uint8(assetInstance.iType));
171
+ }
172
+ return Bytes32.decode(assetInstance.payload);
173
+ }
174
+ }
@@ -0,0 +1,82 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {AssetFilter} from "../AssetFilter/AssetFilter.sol";
5
+ import {AssetFilterCodec} from "../AssetFilter/AssetFilterCodec.sol";
6
+
7
+ /// @notice Discriminant for the `AssetTransferFilter` enum.
8
+ enum AssetTransferFilterType {
9
+ /// @custom:variant Teleport assets matching `AssetFilter` to a specific destination.
10
+ Teleport,
11
+ /// @custom:variant Reserve-transfer assets matching `AssetFilter` to a specific destination, using the local chain as reserve.
12
+ ReserveDeposit,
13
+ /// @custom:variant Reserve-transfer assets matching `AssetFilter` to a specific destination, using the destination as reserve.
14
+ ReserveWithdraw
15
+ }
16
+
17
+ /// @notice Matches assets based on inner `AssetFilter` and tags them for a specific type of asset transfer.
18
+ struct AssetTransferFilter {
19
+ /// @custom:property The type of asset transfer. See `AssetTransferFilterType` enum for possible values.
20
+ AssetTransferFilterType atfType;
21
+ /// @custom:property The SCALE-encoded `AssetFilter` payload.
22
+ bytes payload;
23
+ }
24
+
25
+ /// @notice Parameters for the `Teleport` variant.
26
+ struct TeleportParams {
27
+ /// @custom:property Asset filter used for teleport transfer.
28
+ AssetFilter filter;
29
+ }
30
+
31
+ /// @notice Parameters for the `ReserveDeposit` variant.
32
+ struct ReserveDepositParams {
33
+ /// @custom:property Asset filter used for reserve-deposit transfer.
34
+ AssetFilter filter;
35
+ }
36
+
37
+ /// @notice Parameters for the `ReserveWithdraw` variant.
38
+ struct ReserveWithdrawParams {
39
+ /// @custom:property Asset filter used for reserve-withdraw transfer.
40
+ AssetFilter filter;
41
+ }
42
+
43
+ // ============ Factory Functions ============
44
+
45
+ /// @notice Creates a `Teleport` asset transfer filter.
46
+ /// @param params Parameters for the teleport variant.
47
+ /// @return An `AssetTransferFilter` struct representing the teleport filter.
48
+ function teleport(
49
+ TeleportParams memory params
50
+ ) pure returns (AssetTransferFilter memory) {
51
+ return
52
+ AssetTransferFilter({
53
+ atfType: AssetTransferFilterType.Teleport,
54
+ payload: AssetFilterCodec.encode(params.filter)
55
+ });
56
+ }
57
+
58
+ /// @notice Creates a `ReserveDeposit` asset transfer filter.
59
+ /// @param params Parameters for the reserve-deposit variant.
60
+ /// @return An `AssetTransferFilter` struct representing the reserve deposit filter.
61
+ function reserveDeposit(
62
+ ReserveDepositParams memory params
63
+ ) pure returns (AssetTransferFilter memory) {
64
+ return
65
+ AssetTransferFilter({
66
+ atfType: AssetTransferFilterType.ReserveDeposit,
67
+ payload: AssetFilterCodec.encode(params.filter)
68
+ });
69
+ }
70
+
71
+ /// @notice Creates a `ReserveWithdraw` asset transfer filter.
72
+ /// @param params Parameters for the reserve-withdraw variant.
73
+ /// @return An `AssetTransferFilter` struct representing the reserve withdraw filter.
74
+ function reserveWithdraw(
75
+ ReserveWithdrawParams memory params
76
+ ) pure returns (AssetTransferFilter memory) {
77
+ return
78
+ AssetTransferFilter({
79
+ atfType: AssetTransferFilterType.ReserveWithdraw,
80
+ payload: AssetFilterCodec.encode(params.filter)
81
+ });
82
+ }
@@ -0,0 +1,95 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {AssetFilter} from "../AssetFilter/AssetFilter.sol";
5
+ import {AssetFilterCodec} from "../AssetFilter/AssetFilterCodec.sol";
6
+ import {
7
+ AssetTransferFilter,
8
+ AssetTransferFilterType
9
+ } from "./AssetTransferFilter.sol";
10
+
11
+ /// @title SCALE Codec for XCM v5 `AssetTransferFilter`
12
+ /// @notice SCALE-compliant encoder/decoder for the `AssetTransferFilter` type.
13
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
14
+ /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/enum.AssetTransferFilter.html
15
+ library AssetTransferFilterCodec {
16
+ error InvalidAssetTransferFilterLength();
17
+ error InvalidAssetTransferFilterType(uint8 atfType);
18
+
19
+ /// @notice Encodes an `AssetTransferFilter` struct into SCALE bytes.
20
+ /// @param atf The `AssetTransferFilter` struct to encode.
21
+ /// @return SCALE-encoded bytes representing the `AssetTransferFilter`.
22
+ function encode(
23
+ AssetTransferFilter memory atf
24
+ ) internal pure returns (bytes memory) {
25
+ return abi.encodePacked(uint8(atf.atfType), atf.payload);
26
+ }
27
+
28
+ /// @notice Returns the number of bytes that an `AssetTransferFilter` would occupy when SCALE-encoded.
29
+ /// @param data The byte sequence containing the encoded `AssetTransferFilter`.
30
+ /// @param offset The starting index in `data` from which to calculate the encoded size.
31
+ /// @return The number of bytes occupied by the encoded `AssetTransferFilter`.
32
+ function encodedSizeAt(
33
+ bytes memory data,
34
+ uint256 offset
35
+ ) internal pure returns (uint256) {
36
+ if (data.length < offset + 1) revert InvalidAssetTransferFilterLength();
37
+ uint8 atfType = uint8(data[offset]);
38
+ if (atfType > uint8(AssetTransferFilterType.ReserveWithdraw))
39
+ revert InvalidAssetTransferFilterType(atfType);
40
+ return 1 + AssetFilterCodec.encodedSizeAt(data, offset + 1);
41
+ }
42
+
43
+ /// @notice Decodes an `AssetTransferFilter` from SCALE bytes starting at the beginning.
44
+ /// @param data The byte sequence containing the encoded `AssetTransferFilter`.
45
+ /// @return atf The decoded `AssetTransferFilter` struct.
46
+ /// @return bytesRead The number of bytes read.
47
+ function decode(
48
+ bytes memory data
49
+ )
50
+ internal
51
+ pure
52
+ returns (AssetTransferFilter memory atf, uint256 bytesRead)
53
+ {
54
+ return decodeAt(data, 0);
55
+ }
56
+
57
+ /// @notice Decodes an `AssetTransferFilter` from SCALE bytes starting at a given offset.
58
+ /// @param data The byte sequence containing the encoded `AssetTransferFilter`.
59
+ /// @param offset The starting index in `data` from which to decode.
60
+ /// @return atf The decoded `AssetTransferFilter` struct.
61
+ /// @return bytesRead The number of bytes read.
62
+ function decodeAt(
63
+ bytes memory data,
64
+ uint256 offset
65
+ )
66
+ internal
67
+ pure
68
+ returns (AssetTransferFilter memory atf, uint256 bytesRead)
69
+ {
70
+ if (data.length < offset + 1) revert InvalidAssetTransferFilterLength();
71
+ uint8 atfType = uint8(data[offset]);
72
+ if (atfType > uint8(AssetTransferFilterType.ReserveWithdraw))
73
+ revert InvalidAssetTransferFilterType(atfType);
74
+ uint256 size = encodedSizeAt(data, offset);
75
+ uint256 payloadLength = size - 1;
76
+ bytes memory payload = new bytes(payloadLength);
77
+ for (uint256 i = 0; i < payloadLength; ++i) {
78
+ payload[i] = data[offset + 1 + i];
79
+ }
80
+ atf = AssetTransferFilter({
81
+ atfType: AssetTransferFilterType(atfType),
82
+ payload: payload
83
+ });
84
+ bytesRead = size;
85
+ }
86
+
87
+ /// @notice Extracts the inner `AssetFilter` from an `AssetTransferFilter`.
88
+ /// @param atf The `AssetTransferFilter` struct to decode.
89
+ /// @return The inner `AssetFilter`.
90
+ function asInner(
91
+ AssetTransferFilter memory atf
92
+ ) internal pure returns (AssetFilter memory) {
93
+ return AssetFilterCodec.decode(atf.payload);
94
+ }
95
+ }
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Asset} from "../Asset/Asset.sol";
5
+
6
+ /// @notice An array of Assets.
7
+ /// @dev There are a number of invariants which the construction and mutation functions must ensure are maintained:
8
+ /// - It may contain no items of duplicate asset class.
9
+ /// - All items must be ordered.
10
+ /// - The number of items should grow no larger than `MAX_ITEMS_IN_ASSETS`.
11
+ struct Assets {
12
+ /// @custom:property The items of the array.
13
+ Asset[] items;
14
+ }
@@ -0,0 +1,94 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Asset} from "../Asset/Asset.sol";
5
+ import {AssetCodec} from "../Asset/AssetCodec.sol";
6
+ import {Compact} from "../../../Scale/Compact.sol";
7
+ import {MAX_ITEMS_IN_ASSETS} from "../Constants.sol";
8
+ import {Assets} from "./Assets.sol";
9
+
10
+ /// @title SCALE Codec for XCM v5 `Assets`
11
+ /// @notice SCALE-compliant encoder/decoder for the `Assets` type.
12
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
13
+ /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/index.html
14
+ library AssetsCodec {
15
+ error InvalidAssetsLength();
16
+ error InvalidAssetsPayload();
17
+
18
+ /// @notice Encodes an `Assets` struct into bytes.
19
+ /// @param assets The `Assets` struct to encode. Assumes that the `items` array is properly constructed according to the invariants specified in the `Assets` struct definition.
20
+ /// @return SCALE-encoded byte sequence representing the `Assets`.
21
+ function encode(Assets memory assets) internal pure returns (bytes memory) {
22
+ bytes memory encoded = Compact.encode(assets.items.length);
23
+ for (uint256 i = 0; i < assets.items.length; i++) {
24
+ encoded = abi.encodePacked(
25
+ encoded,
26
+ AssetCodec.encode(assets.items[i])
27
+ );
28
+ }
29
+ return encoded;
30
+ }
31
+
32
+ /// @notice Returns the number of bytes that an `Assets` struct would occupy when SCALE-encoded.
33
+ /// @param data The byte sequence containing the encoded `Assets`.
34
+ /// @param offset The starting index in `data` from which to calculate the encoded size of the `Assets`.
35
+ /// @return The number of bytes that the `Assets` struct would occupy when SCALE-encoded.
36
+ function encodedSizeAt(
37
+ bytes memory data,
38
+ uint256 offset
39
+ ) internal pure returns (uint256) {
40
+ if (data.length < offset + 1) {
41
+ revert InvalidAssetsLength();
42
+ }
43
+ (uint256 length, uint256 bytesRead) = Compact.decodeAt(data, offset);
44
+ uint256 currentOffset = offset + bytesRead;
45
+ for (uint256 i = 0; i < length; i++) {
46
+ uint256 assetSize = AssetCodec.encodedSizeAt(data, currentOffset);
47
+ currentOffset += assetSize;
48
+ }
49
+ return currentOffset - offset;
50
+ }
51
+
52
+ /// @notice Decodes an `Assets` struct from bytes starting at the beginning of the data.
53
+ /// @param data The byte sequence containing the encoded `Assets`.
54
+ /// @return assets The decoded `Assets` struct.
55
+ /// @return bytesRead The total number of bytes read from `data` to decode the `Assets`.
56
+ function decode(
57
+ bytes memory data
58
+ ) internal pure returns (Assets memory assets, uint256 bytesRead) {
59
+ return decodeAt(data, 0);
60
+ }
61
+
62
+ /// @notice Decodes an `Assets` struct from bytes starting at a given offset.
63
+ /// @param data The byte sequence containing the encoded `Assets`.
64
+ /// @param offset The starting index in `data` from which to decode the `Assets`.
65
+ /// @return assets The decoded `Assets` struct.
66
+ /// @return bytesRead The total number of bytes read from `data` to decode the `Assets`.
67
+ function decodeAt(
68
+ bytes memory data,
69
+ uint256 offset
70
+ ) internal pure returns (Assets memory assets, uint256 bytesRead) {
71
+ if (data.length < offset + 1) {
72
+ revert InvalidAssetsLength();
73
+ }
74
+ (uint256 length, uint256 compactBytesRead) = Compact.decodeAt(
75
+ data,
76
+ offset
77
+ );
78
+ if (length > MAX_ITEMS_IN_ASSETS) {
79
+ revert InvalidAssetsPayload();
80
+ }
81
+ Asset[] memory items = new Asset[](length);
82
+ uint256 currentOffset = offset + compactBytesRead;
83
+ for (uint256 i = 0; i < length; i++) {
84
+ (Asset memory asset, uint256 assetBytesRead) = AssetCodec.decodeAt(
85
+ data,
86
+ currentOffset
87
+ );
88
+ items[i] = asset;
89
+ currentOffset += assetBytesRead;
90
+ }
91
+ assets = Assets({items: items});
92
+ bytesRead = 1 + currentOffset - offset - 1;
93
+ }
94
+ }
@@ -0,0 +1,121 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Bytes4} from "../../../Scale/Bytes.sol";
5
+ import {Compact} from "../../../Scale/Compact.sol";
6
+
7
+ /// @dev Discriminant for the different types of BodyIds in XCM v5. Each variant corresponds to a specific structure of the payload.
8
+ enum BodyIdType {
9
+ /// @custom:variant The only body in its context.
10
+ Unit,
11
+ /// @custom:variant A named body.
12
+ Moniker,
13
+ /// @custom:variant An indexed body.
14
+ Index,
15
+ /// @custom:variant The unambiguous executive body (for Polkadot, this would be the Polkadot council).
16
+ Executive,
17
+ /// @custom:variant The unambiguous technical body (for Polkadot, this would be the Technical Committee).
18
+ Technical,
19
+ /// @custom:variant The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of lock-voters).
20
+ Legislative,
21
+ /// @custom:variant The unambiguous judicial body (this doesn’t exist on Polkadot, but if it were to get a “grand oracle”, it may be considered as that).
22
+ Judicial,
23
+ /// @custom:variant The unambiguous defense body (for Polkadot, an opinion on the topic given via a public referendum on the `staking_admin` track).
24
+ Defense,
25
+ /// @custom:variant The unambiguous administration body (for Polkadot, an opinion on the topic given via a public referendum on the `general_admin` track).
26
+ Administration,
27
+ /// @custom:variant The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public referendum on the `treasurer` track).
28
+ Treasury
29
+ }
30
+
31
+ /// @notice An identifier of a pluralistic body.
32
+ struct BodyId {
33
+ /// @custom:property The type of BodyId, which determines how to interpret the payload
34
+ BodyIdType bodyIdType;
35
+ /// @custom:property For Moniker and Index types, this will hold the relevant data
36
+ bytes payload;
37
+ }
38
+
39
+ /// @notice Parameters for the `Moniker` variant.
40
+ struct MonikerParams {
41
+ /// @custom:property The 4-byte name of the moniker body.
42
+ bytes4 name;
43
+ }
44
+
45
+ /// @notice Parameters for the `Index` variant.
46
+ struct IndexParams {
47
+ /// @custom:property The index of the body.
48
+ uint32 idx;
49
+ }
50
+
51
+ // ============ Factory Functions ============
52
+
53
+ /// @notice Creates a `BodyId` representing a `Unit` body.
54
+ /// @return A `BodyId` with `bodyIdType` set to `Unit` and an empty payload.
55
+ function unit() pure returns (BodyId memory) {
56
+ return BodyId({bodyIdType: BodyIdType.Unit, payload: ""});
57
+ }
58
+
59
+ /// @notice Creates a `BodyId` representing a `Moniker` body with the given 4-byte name.
60
+ /// @param params Parameters for the moniker variant.
61
+ /// @return A `BodyId` with `bodyIdType` set to `Moniker` and the provided name encoded in the payload.
62
+ function moniker(MonikerParams memory params) pure returns (BodyId memory) {
63
+ return
64
+ BodyId({
65
+ bodyIdType: BodyIdType.Moniker,
66
+ payload: Bytes4.encode(params.name)
67
+ });
68
+ }
69
+
70
+ /// @notice Creates a `BodyId` representing an `Index` body with the given index.
71
+ /// @param params Parameters for the index variant.
72
+ /// @return A `BodyId` with `bodyIdType` set to `Index` and the provided index encoded in the payload.
73
+ function index(IndexParams memory params) pure returns (BodyId memory) {
74
+ return
75
+ BodyId({
76
+ bodyIdType: BodyIdType.Index,
77
+ payload: Compact.encode(params.idx)
78
+ });
79
+ }
80
+
81
+ /// @notice Creates a `BodyId` representing an `Executive` body.
82
+ /// @return A `BodyId` with `bodyIdType` set to `Executive` and an empty payload.
83
+ function executive() pure returns (BodyId memory) {
84
+ return BodyId({bodyIdType: BodyIdType.Executive, payload: ""});
85
+ }
86
+
87
+ /// @notice Creates a `BodyId` representing a `Technical` body.
88
+ /// @return A `BodyId` with `bodyIdType` set to `Technical` and an empty payload.
89
+ function technical() pure returns (BodyId memory) {
90
+ return BodyId({bodyIdType: BodyIdType.Technical, payload: ""});
91
+ }
92
+
93
+ /// @notice Creates a `BodyId` representing a `Legislative` body.
94
+ /// @return A `BodyId` with `bodyIdType` set to `Legislative` and an empty payload.
95
+ function legislative() pure returns (BodyId memory) {
96
+ return BodyId({bodyIdType: BodyIdType.Legislative, payload: ""});
97
+ }
98
+
99
+ /// @notice Creates a `BodyId` representing a `Judicial` body.
100
+ /// @return A `BodyId` with `bodyIdType` set to `Judicial` and an empty payload.
101
+ function judicial() pure returns (BodyId memory) {
102
+ return BodyId({bodyIdType: BodyIdType.Judicial, payload: ""});
103
+ }
104
+
105
+ /// @notice Creates a `BodyId` representing a `Defense` body.
106
+ /// @return A `BodyId` with `bodyIdType` set to `Defense` and an empty payload.
107
+ function defense() pure returns (BodyId memory) {
108
+ return BodyId({bodyIdType: BodyIdType.Defense, payload: ""});
109
+ }
110
+
111
+ /// @notice Creates a `BodyId` representing an `Administration` body.
112
+ /// @return A `BodyId` with `bodyIdType` set to `Administration` and an empty payload.
113
+ function administration() pure returns (BodyId memory) {
114
+ return BodyId({bodyIdType: BodyIdType.Administration, payload: ""});
115
+ }
116
+
117
+ /// @notice Creates a `BodyId` representing a `Treasury` body.
118
+ /// @return A `BodyId` with `bodyIdType` set to `Treasury` and an empty payload.
119
+ function treasury() pure returns (BodyId memory) {
120
+ return BodyId({bodyIdType: BodyIdType.Treasury, payload: ""});
121
+ }