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,321 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Instruction, InstructionType} from "./Instruction.sol";
5
+ import {AssetsCodec} from "../Assets/AssetsCodec.sol";
6
+ import {AssetCodec} from "../Asset/AssetCodec.sol";
7
+ import {LocationCodec} from "../Location/LocationCodec.sol";
8
+ import {JunctionsCodec} from "../Junctions/JunctionsCodec.sol";
9
+ import {JunctionCodec} from "../Junction/JunctionCodec.sol";
10
+ import {AssetFilterCodec} from "../AssetFilter/AssetFilterCodec.sol";
11
+ import {AssetTransferFilterCodec} from "../AssetTransferFilter/AssetTransferFilterCodec.sol";
12
+ import {QueryResponseInfoCodec} from "../QueryResponseInfo/QueryResponseInfoCodec.sol";
13
+ import {ResponseCodec} from "../Response/ResponseCodec.sol";
14
+ import {XcmErrorCodec} from "../XcmError/XcmErrorCodec.sol";
15
+ import {NetworkIdCodec} from "../NetworkId/NetworkIdCodec.sol";
16
+ import {OriginKindCodec} from "../OriginKind/OriginKindCodec.sol";
17
+ import {WeightCodec} from "../Weight/WeightCodec.sol";
18
+ import {WeightLimitCodec} from "../WeightLimit/WeightLimitCodec.sol";
19
+ import {HintCodec} from "../Hint/HintCodec.sol";
20
+ import {MaybeErrorCodeCodec} from "../../v3/MaybeErrorCode/MaybeErrorCodeCodec.sol";
21
+ import {MAX_ASSET_TRANSFER_FILTERS, HINT_NUM_VARIANTS} from "../Constants.sol";
22
+
23
+ import {Compact} from "../../../Scale/Compact.sol";
24
+ import {Bool} from "../../../Scale/Bool/Bool.sol";
25
+ import {Bytes} from "../../../Scale/Bytes/Bytes.sol";
26
+
27
+ /// @title SCALE Codec for XCM v5 `Instruction`
28
+ /// @notice SCALE-compliant encoder/decoder for the `Instruction` type.
29
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
30
+ /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/index.html
31
+ library InstructionCodec {
32
+ error InvalidInstructionLength();
33
+ error InvalidInstructionType(uint8 iType);
34
+ error InvalidInstructionPayload();
35
+
36
+ /// @notice Encodes an `Instruction` into SCALE bytes.
37
+ /// @param instruction The `Instruction` struct to encode.
38
+ /// @return SCALE-encoded bytes representing the instruction.
39
+ function encode(
40
+ Instruction memory instruction
41
+ ) internal pure returns (bytes memory) {
42
+ return abi.encodePacked(uint8(instruction.iType), instruction.payload);
43
+ }
44
+
45
+ /// @notice Returns the number of bytes that an `Instruction` occupies when SCALE-encoded.
46
+ /// @param data The byte sequence containing the encoded instruction.
47
+ /// @param offset The starting index in `data` from which to calculate the encoded size.
48
+ /// @return The number of bytes occupied by the encoded instruction.
49
+ function encodedSizeAt(
50
+ bytes memory data,
51
+ uint256 offset
52
+ ) internal pure returns (uint256) {
53
+ if (data.length < offset + 1) revert InvalidInstructionLength();
54
+
55
+ uint8 iTypeRaw = uint8(data[offset]);
56
+ if (iTypeRaw > uint8(InstructionType.SetHints)) {
57
+ revert InvalidInstructionType(iTypeRaw);
58
+ }
59
+
60
+ InstructionType iType = InstructionType(iTypeRaw);
61
+ uint256 pos = offset + 1;
62
+
63
+ if (
64
+ iType == InstructionType.WithdrawAsset ||
65
+ iType == InstructionType.ReserveAssetDeposited ||
66
+ iType == InstructionType.ReceiveTeleportedAsset ||
67
+ iType == InstructionType.BurnAsset ||
68
+ iType == InstructionType.ExpectAsset
69
+ ) {
70
+ pos += AssetsCodec.encodedSizeAt(data, pos);
71
+ } else if (iType == InstructionType.QueryResponse) {
72
+ pos += Compact.encodedSizeAt(data, pos);
73
+ pos += ResponseCodec.encodedSizeAt(data, pos);
74
+ pos += WeightCodec.encodedSizeAt(data, pos);
75
+ bool hasQuerier = Bool.decodeAt(data, pos);
76
+ pos += 1;
77
+ if (hasQuerier) {
78
+ pos += LocationCodec.encodedSizeAt(data, pos);
79
+ }
80
+ } else if (iType == InstructionType.TransferAsset) {
81
+ pos += AssetsCodec.encodedSizeAt(data, pos);
82
+ pos += LocationCodec.encodedSizeAt(data, pos);
83
+ } else if (
84
+ iType == InstructionType.TransferReserveAsset ||
85
+ iType == InstructionType.DepositReserveAsset ||
86
+ iType == InstructionType.InitiateReserveWithdraw ||
87
+ iType == InstructionType.InitiateTeleport
88
+ ) {
89
+ if (iType == InstructionType.TransferReserveAsset) {
90
+ pos += AssetsCodec.encodedSizeAt(data, pos);
91
+ pos += LocationCodec.encodedSizeAt(data, pos);
92
+ } else {
93
+ pos += AssetFilterCodec.encodedSizeAt(data, pos);
94
+ pos += LocationCodec.encodedSizeAt(data, pos);
95
+ }
96
+ pos += _xcmEncodedSizeAt(data, pos);
97
+ } else if (iType == InstructionType.Transact) {
98
+ pos += OriginKindCodec.encodedSizeAt(data, pos);
99
+ bool hasFallbackMaxWeight = Bool.decodeAt(data, pos);
100
+ pos += 1;
101
+ if (hasFallbackMaxWeight) {
102
+ pos += WeightCodec.encodedSizeAt(data, pos);
103
+ }
104
+ pos += Bytes.encodedSizeAt(data, pos);
105
+ } else if (iType == InstructionType.HrmpNewChannelOpenRequest) {
106
+ pos += Compact.encodedSizeAt(data, pos);
107
+ pos += Compact.encodedSizeAt(data, pos);
108
+ pos += Compact.encodedSizeAt(data, pos);
109
+ } else if (iType == InstructionType.HrmpChannelAccepted) {
110
+ pos += Compact.encodedSizeAt(data, pos);
111
+ } else if (iType == InstructionType.HrmpChannelClosing) {
112
+ pos += Compact.encodedSizeAt(data, pos);
113
+ pos += Compact.encodedSizeAt(data, pos);
114
+ pos += Compact.encodedSizeAt(data, pos);
115
+ } else if (
116
+ iType == InstructionType.ClearOrigin ||
117
+ iType == InstructionType.RefundSurplus ||
118
+ iType == InstructionType.ClearError ||
119
+ iType == InstructionType.UnsubscribeVersion ||
120
+ iType == InstructionType.ClearTransactStatus ||
121
+ iType == InstructionType.ClearTopic
122
+ ) {
123
+ // no payload
124
+ } else if (iType == InstructionType.DescendOrigin) {
125
+ pos += JunctionsCodec.encodedSizeAt(data, pos);
126
+ } else if (
127
+ iType == InstructionType.ReportError ||
128
+ iType == InstructionType.ReportTransactStatus
129
+ ) {
130
+ pos += QueryResponseInfoCodec.encodedSizeAt(data, pos);
131
+ } else if (iType == InstructionType.DepositAsset) {
132
+ pos += AssetFilterCodec.encodedSizeAt(data, pos);
133
+ pos += LocationCodec.encodedSizeAt(data, pos);
134
+ } else if (iType == InstructionType.ExchangeAsset) {
135
+ pos += AssetFilterCodec.encodedSizeAt(data, pos);
136
+ pos += AssetsCodec.encodedSizeAt(data, pos);
137
+ pos += 1;
138
+ } else if (iType == InstructionType.ReportHolding) {
139
+ pos += QueryResponseInfoCodec.encodedSizeAt(data, pos);
140
+ pos += AssetFilterCodec.encodedSizeAt(data, pos);
141
+ } else if (iType == InstructionType.BuyExecution) {
142
+ pos += AssetCodec.encodedSizeAt(data, pos);
143
+ pos += WeightLimitCodec.encodedSizeAt(data, pos);
144
+ } else if (
145
+ iType == InstructionType.SetErrorHandler ||
146
+ iType == InstructionType.SetAppendix
147
+ ) {
148
+ pos += _xcmEncodedSizeAt(data, pos);
149
+ } else if (iType == InstructionType.ClaimAsset) {
150
+ pos += AssetsCodec.encodedSizeAt(data, pos);
151
+ pos += LocationCodec.encodedSizeAt(data, pos);
152
+ } else if (iType == InstructionType.Trap) {
153
+ pos += Compact.encodedSizeAt(data, pos);
154
+ } else if (iType == InstructionType.SubscribeVersion) {
155
+ pos += Compact.encodedSizeAt(data, pos);
156
+ pos += WeightCodec.encodedSizeAt(data, pos);
157
+ } else if (iType == InstructionType.ExpectOrigin) {
158
+ bool hasOrigin = Bool.decodeAt(data, pos);
159
+ pos += 1;
160
+ if (hasOrigin) {
161
+ pos += LocationCodec.encodedSizeAt(data, pos);
162
+ }
163
+ } else if (iType == InstructionType.ExpectError) {
164
+ bool hasError = Bool.decodeAt(data, pos);
165
+ pos += 1;
166
+ if (hasError) {
167
+ pos += Compact.encodedSizeAt(data, pos);
168
+ pos += XcmErrorCodec.encodedSizeAt(data, pos);
169
+ }
170
+ } else if (iType == InstructionType.ExpectTransactStatus) {
171
+ pos += MaybeErrorCodeCodec.encodedSizeAt(data, pos);
172
+ } else if (iType == InstructionType.QueryPallet) {
173
+ pos += Bytes.encodedSizeAt(data, pos);
174
+ pos += QueryResponseInfoCodec.encodedSizeAt(data, pos);
175
+ } else if (iType == InstructionType.ExpectPallet) {
176
+ pos += Compact.encodedSizeAt(data, pos);
177
+ pos += Bytes.encodedSizeAt(data, pos);
178
+ pos += Bytes.encodedSizeAt(data, pos);
179
+ pos += Compact.encodedSizeAt(data, pos);
180
+ pos += Compact.encodedSizeAt(data, pos);
181
+ } else if (iType == InstructionType.UniversalOrigin) {
182
+ pos += JunctionCodec.encodedSizeAt(data, pos);
183
+ } else if (iType == InstructionType.ExportMessage) {
184
+ pos += NetworkIdCodec.encodedSizeAt(data, pos);
185
+ pos += JunctionsCodec.encodedSizeAt(data, pos);
186
+ pos += _xcmEncodedSizeAt(data, pos);
187
+ } else if (
188
+ iType == InstructionType.LockAsset ||
189
+ iType == InstructionType.UnlockAsset ||
190
+ iType == InstructionType.NoteUnlockable ||
191
+ iType == InstructionType.RequestUnlock
192
+ ) {
193
+ pos += AssetCodec.encodedSizeAt(data, pos);
194
+ pos += LocationCodec.encodedSizeAt(data, pos);
195
+ } else if (iType == InstructionType.SetFeesMode) {
196
+ pos += 1;
197
+ } else if (iType == InstructionType.SetTopic) {
198
+ if (data.length < pos + 32) revert InvalidInstructionPayload();
199
+ pos += 32;
200
+ } else if (iType == InstructionType.AliasOrigin) {
201
+ pos += LocationCodec.encodedSizeAt(data, pos);
202
+ } else if (iType == InstructionType.UnpaidExecution) {
203
+ pos += WeightLimitCodec.encodedSizeAt(data, pos);
204
+ bool hasCheckOrigin = Bool.decodeAt(data, pos);
205
+ pos += 1;
206
+ if (hasCheckOrigin) {
207
+ pos += LocationCodec.encodedSizeAt(data, pos);
208
+ }
209
+ } else if (iType == InstructionType.PayFees) {
210
+ pos += AssetCodec.encodedSizeAt(data, pos);
211
+ } else if (iType == InstructionType.InitiateTransfer) {
212
+ pos += LocationCodec.encodedSizeAt(data, pos);
213
+ bool hasRemoteFees = Bool.decodeAt(data, pos);
214
+ pos += 1;
215
+ if (hasRemoteFees) {
216
+ pos += AssetTransferFilterCodec.encodedSizeAt(data, pos);
217
+ }
218
+ pos += 1; // preserveOrigin bool
219
+ (uint256 assetsCount, uint256 assetsCountBytes) = Compact.decodeAt(
220
+ data,
221
+ pos
222
+ );
223
+ if (assetsCount > MAX_ASSET_TRANSFER_FILTERS) {
224
+ revert InvalidInstructionPayload();
225
+ }
226
+ pos += assetsCountBytes;
227
+ for (uint256 i = 0; i < assetsCount; ++i) {
228
+ pos += AssetTransferFilterCodec.encodedSizeAt(data, pos);
229
+ }
230
+ (uint256 remoteXcmLen, uint256 remoteXcmLenBytes) = Compact
231
+ .decodeAt(data, pos);
232
+ pos += remoteXcmLenBytes;
233
+ if (data.length < pos + remoteXcmLen)
234
+ revert InvalidInstructionPayload();
235
+ pos += remoteXcmLen;
236
+ } else if (iType == InstructionType.ExecuteWithOrigin) {
237
+ bool hasDescendantOrigin = Bool.decodeAt(data, pos);
238
+ pos += 1;
239
+ if (hasDescendantOrigin) {
240
+ pos += JunctionsCodec.encodedSizeAt(data, pos);
241
+ }
242
+ pos += _xcmEncodedSizeAt(data, pos);
243
+ } else if (iType == InstructionType.SetHints) {
244
+ (uint256 hintsCount, uint256 hintsCountBytes) = Compact.decodeAt(
245
+ data,
246
+ pos
247
+ );
248
+ if (hintsCount > HINT_NUM_VARIANTS) {
249
+ revert InvalidInstructionPayload();
250
+ }
251
+ pos += hintsCountBytes;
252
+ for (uint256 i = 0; i < hintsCount; ++i) {
253
+ pos += HintCodec.encodedSizeAt(data, pos);
254
+ }
255
+ }
256
+
257
+ return pos - offset;
258
+ }
259
+
260
+ /// @notice Decodes an `Instruction` from SCALE bytes starting at the beginning.
261
+ /// @param data The byte sequence containing the encoded instruction.
262
+ /// @return instruction The decoded `Instruction` struct.
263
+ /// @return bytesRead The number of bytes read.
264
+ function decode(
265
+ bytes memory data
266
+ )
267
+ internal
268
+ pure
269
+ returns (Instruction memory instruction, uint256 bytesRead)
270
+ {
271
+ return decodeAt(data, 0);
272
+ }
273
+
274
+ /// @notice Decodes an `Instruction` from SCALE bytes starting at a given offset.
275
+ /// @param data The byte sequence containing the encoded instruction.
276
+ /// @param offset The starting index in `data` from which to decode.
277
+ /// @return instruction The decoded `Instruction` struct.
278
+ /// @return bytesRead The number of bytes read.
279
+ function decodeAt(
280
+ bytes memory data,
281
+ uint256 offset
282
+ )
283
+ internal
284
+ pure
285
+ returns (Instruction memory instruction, uint256 bytesRead)
286
+ {
287
+ if (data.length < offset + 1) revert InvalidInstructionLength();
288
+
289
+ uint8 iTypeRaw = uint8(data[offset]);
290
+ if (iTypeRaw > uint8(InstructionType.SetHints)) {
291
+ revert InvalidInstructionType(iTypeRaw);
292
+ }
293
+
294
+ uint256 size = encodedSizeAt(data, offset);
295
+ uint256 payloadLength = size - 1;
296
+ bytes memory payload = new bytes(payloadLength);
297
+ for (uint256 i = 0; i < payloadLength; ++i) {
298
+ payload[i] = data[offset + 1 + i];
299
+ }
300
+
301
+ instruction = Instruction({
302
+ iType: InstructionType(iTypeRaw),
303
+ payload: payload
304
+ });
305
+ bytesRead = size;
306
+ }
307
+
308
+ /// @notice Returns the encoded size of an XCM byte sequence at offset.
309
+ /// @dev XCM encodes as `Vec<Instruction>`: compact count followed by encoded instructions.
310
+ function _xcmEncodedSizeAt(
311
+ bytes memory data,
312
+ uint256 offset
313
+ ) private pure returns (uint256) {
314
+ (uint256 count, uint256 countBytes) = Compact.decodeAt(data, offset);
315
+ uint256 pos = offset + countBytes;
316
+ for (uint256 i = 0; i < count; ++i) {
317
+ pos += encodedSizeAt(data, pos);
318
+ }
319
+ return pos - offset;
320
+ }
321
+ }
@@ -0,0 +1,258 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {BodyId} from "../BodyId/BodyId.sol";
5
+ import {BodyIdCodec} from "../BodyId/BodyIdCodec.sol";
6
+ import {BodyPart} from "../BodyPart/BodyPart.sol";
7
+ import {BodyPartCodec} from "../BodyPart/BodyPartCodec.sol";
8
+ import {NetworkId} from "../NetworkId/NetworkId.sol";
9
+ import {NetworkIdCodec} from "../NetworkId/NetworkIdCodec.sol";
10
+ import {Bytes32} from "../../../Scale/Bytes.sol";
11
+ import {Address} from "../../../Scale/Address.sol";
12
+ import {Compact} from "../../../Scale/Compact.sol";
13
+
14
+ /// @dev Discriminant for the different types of junctions in XCM v5. Each variant corresponds to a specific structure of the payload.
15
+ enum JunctionType {
16
+ /// @custom:variant An indexed parachain belonging to and operated by the context.
17
+ Parachain,
18
+ /// @custom:variantA 32-byte identifier for an account of a specific network that is respected as a sovereign endpoint within the context.
19
+ AccountId32,
20
+ /// @custom:variant Generally used when the context is a Frame-based chain.
21
+ AccountIndex64,
22
+ /// @custom:variant A 20-byte identifier for an account of a specific network that is respected as a sovereign endpoint within the context.
23
+ AccountKey20,
24
+ /// @custom:variant An instanced, indexed pallet that forms a constituent part of the context.
25
+ PalletInstance,
26
+ /// @custom:variant A non-descript index within the context location. NOTE: Try to avoid using this and instead use a more specific item.
27
+ GeneralIndex,
28
+ /// @custom:variant A nondescript array datum, 32 bytes, acting as a key within the context location. NOTE: Try to avoid using this and instead use a more specific item.
29
+ GeneralKey,
30
+ /// @custom:variant The unambiguous child. Not currently used except as a fallback when deriving context.
31
+ OnlyChild,
32
+ /// @custom:variant A pluralistic body existing within consensus.
33
+ Plurality,
34
+ /// @custom:variant A global network capable of externalizing its own consensus. This is not generally meaningful outside of the universal level.
35
+ GlobalConsensus
36
+ }
37
+
38
+ /// @notice Parameters for an `AccountId32` junction, containing optional network information and a 32-byte account identifier.
39
+ struct AccountId32Params {
40
+ /// @custom:property Indicates whether the junction includes network information. If true, the `network` field contains valid data; if false, the `network` field should be ignored.
41
+ bool hasNetwork;
42
+ /// @custom:property The `NetworkId` associated with the account, See `NetworkId` struct for details. This field is only meaningful if `hasNetwork` is true.
43
+ NetworkId network;
44
+ /// @custom:property The 32-byte identifier for the account.
45
+ bytes32 id;
46
+ }
47
+
48
+ /// @notice Parameters for an `Plurality` junction
49
+ struct PluralityParams {
50
+ /// @custom:property The identifier for the body of the plurality. See `BodyId` enum for details.
51
+ BodyId id;
52
+ /// @custom:property The part of the body that is relevant for this junction. See `BodyPart` struct for details.
53
+ BodyPart part;
54
+ }
55
+
56
+ /// @notice Parameters for an `AccountIndex64` junction, containing optional network information and a 64-bit account index.
57
+ struct AccountIndex64Params {
58
+ /// @custom:property Indicates whether the junction includes network information. If true, the `network` field contains valid data; if false, the `network` field should be ignored.
59
+ bool hasNetwork;
60
+ /// @custom:property Indicates whether the junction includes network information. If true, the `network` field contains valid data; if false, the `network` field should be ignored.
61
+ NetworkId network;
62
+ /// @custom:property The 8-byte index identifier for the account.
63
+ uint64 index;
64
+ }
65
+
66
+ /// @notice Parameters for an `AccountKey20` junction, containing optional network information and a 20-byte account key.
67
+ struct AccountKey20Params {
68
+ /// @custom:property Indicates whether the junction includes network information. If true, the `network` field contains valid data; if false, the `network` field should be ignored.
69
+ bool hasNetwork;
70
+ /// @custom:property Indicates whether the junction includes network information. If true, the `network` field contains valid data; if false, the `network` field should be ignored.
71
+ NetworkId network;
72
+ /// @custom:property The 20-byte key identifier for the account, represented as an `address` in Solidity.
73
+ address key;
74
+ }
75
+
76
+ /// @notice Parameters for an `GeneralKey` junction
77
+ struct GeneralKeyParams {
78
+ /// @custom:property The length of the byte array acting as a key within the context location. This should be between 1 and 32, inclusive, to ensure valid encoding and decoding.
79
+ uint8 length;
80
+ /// @custom:property The byte array acting as a key within the context location.
81
+ bytes32 key;
82
+ }
83
+
84
+ /// @notice Parameters for a `Parachain` junction.
85
+ struct ParachainParams {
86
+ /// @custom:property The parachain identifier.
87
+ uint32 parachainId;
88
+ }
89
+
90
+ /// @notice Parameters for a `PalletInstance` junction.
91
+ struct PalletInstanceParams {
92
+ /// @custom:property The pallet instance index.
93
+ uint8 instance;
94
+ }
95
+
96
+ /// @notice Parameters for a `GeneralIndex` junction.
97
+ struct GeneralIndexParams {
98
+ /// @custom:property The general compact index within context.
99
+ uint128 index;
100
+ }
101
+
102
+ /// @notice A single item in a path to describe the relative location of a consensus system. Each item assumes a pre-existing location as its context and is defined in terms of it.
103
+ struct Junction {
104
+ /// @custom:property jType The type of the junction, determining how to interpret the payload. See `JunctionType` enum for possible values.
105
+ JunctionType jType;
106
+ /// @custom:property payload The SCALE-encoded data specific to the junction type. The structure of this data varies based on `jType`.
107
+ bytes payload;
108
+ }
109
+
110
+ error InvalidJunctionPayload();
111
+
112
+ using Address for address;
113
+ using Bytes32 for bytes32;
114
+ using NetworkIdCodec for NetworkId;
115
+
116
+ // ============ Factory Functions ============
117
+
118
+ /// @notice Creates a `Parachain` junction with the given parachain ID.
119
+ /// @param params Parameters for the parachain variant.
120
+ /// @return A `Junction` struct representing the parachain junction.
121
+ function parachain(
122
+ ParachainParams memory params
123
+ ) pure returns (Junction memory) {
124
+ return
125
+ Junction({
126
+ jType: JunctionType.Parachain,
127
+ payload: Compact.encode(params.parachainId)
128
+ });
129
+ }
130
+
131
+ /// @notice Creates an `AccountId32` junction with the specified parameters.
132
+ /// @param hasNetwork A boolean indicating whether the junction includes network information.
133
+ /// @param network The `NetworkId` associated with the account, if `hasNetwork` is true.
134
+ /// @param id The 32-byte identifier for the account.
135
+ /// @return A `Junction` struct representing the `AccountId32` junction with the provided parameters.
136
+ function accountId32(
137
+ bool hasNetwork,
138
+ NetworkId memory network,
139
+ bytes32 id
140
+ ) pure returns (Junction memory) {
141
+ return
142
+ Junction({
143
+ jType: JunctionType.AccountId32,
144
+ payload: abi.encodePacked(hasNetwork, network.encode(), id.encode())
145
+ });
146
+ }
147
+
148
+ /// @notice Creates an `AccountIndex64` junction with the specified parameters.
149
+ /// @param hasNetwork A boolean indicating whether the junction includes network information.
150
+ /// @param network The `NetworkId` associated with the account, if `hasNetwork` is true.
151
+ /// @param index The 64-bit index identifier for the account.
152
+ /// @return A `Junction` struct representing the `AccountIndex64` junction with the provided parameters.
153
+ function accountIndex64(
154
+ bool hasNetwork,
155
+ NetworkId memory network,
156
+ uint64 index
157
+ ) pure returns (Junction memory) {
158
+ return
159
+ Junction({
160
+ jType: JunctionType.AccountIndex64,
161
+ payload: abi.encodePacked(
162
+ hasNetwork,
163
+ network.encode(),
164
+ Compact.encode(index)
165
+ )
166
+ });
167
+ }
168
+
169
+ /// @notice Creates an `AccountKey20` junction with the specified parameters.
170
+ /// @param hasNetwork A boolean indicating whether the junction includes network information.
171
+ /// @param network The `NetworkId` associated with the account, if `hasNetwork` is true.
172
+ /// @param key The 20-byte key identifier for the account, represented as an `address` in Solidity.
173
+ /// @return A `Junction` struct representing the `AccountKey20` junction with the provided parameters.
174
+ function accountKey20(
175
+ bool hasNetwork,
176
+ NetworkId memory network,
177
+ address key
178
+ ) pure returns (Junction memory) {
179
+ return
180
+ Junction({
181
+ jType: JunctionType.AccountKey20,
182
+ payload: abi.encodePacked(
183
+ hasNetwork,
184
+ network.encode(),
185
+ key.encode()
186
+ )
187
+ });
188
+ }
189
+
190
+ /// @notice Creates a `PalletInstance` junction with the given instance index.
191
+ /// @param params Parameters for the pallet-instance variant.
192
+ /// @return A `Junction` struct representing the pallet instance junction.
193
+ function palletInstance(
194
+ PalletInstanceParams memory params
195
+ ) pure returns (Junction memory) {
196
+ return
197
+ Junction({
198
+ jType: JunctionType.PalletInstance,
199
+ payload: abi.encodePacked(params.instance)
200
+ });
201
+ }
202
+
203
+ /// @notice Creates a `GeneralIndex` junction with the given index.
204
+ /// @param params Parameters for the general-index variant.
205
+ /// @return A `Junction` struct representing the general index junction.
206
+ function generalIndex(
207
+ GeneralIndexParams memory params
208
+ ) pure returns (Junction memory) {
209
+ return
210
+ Junction({
211
+ jType: JunctionType.GeneralIndex,
212
+ payload: Compact.encode(params.index)
213
+ });
214
+ }
215
+
216
+ /// @notice Creates a `GeneralKey` junction with the given key.
217
+ /// @param length The byte array acting as a key within the context location. This should be between 1 and 32 bytes in length.
218
+ /// @param key The byte array acting as a key within the context location, represented as a `bytes32` in Solidity. Only the first `length` bytes will be used.
219
+ /// @return A `Junction` struct representing the general key junction with the provided parameters.
220
+ function generalKey(uint8 length, bytes32 key) pure returns (Junction memory) {
221
+ if (length == 0 || length > 32 || key.length != length)
222
+ revert InvalidJunctionPayload();
223
+ return
224
+ Junction({
225
+ jType: JunctionType.GeneralKey,
226
+ payload: abi.encodePacked(length, key)
227
+ });
228
+ }
229
+
230
+ /// @notice Creates an `OnlyChild` junction, which represents the unambiguous child in the context.
231
+ /// @return A `Junction` struct representing the `OnlyChild` junction, with an empty payload.
232
+ function onlyChild() pure returns (Junction memory) {
233
+ return Junction({jType: JunctionType.OnlyChild, payload: ""});
234
+ }
235
+
236
+ /// @notice Creates a `Plurality` junction with the specified body ID and body part.
237
+ /// @param id The identifier for the body of the plurality, represented as a `BodyId` struct.
238
+ /// @param part The part of the body that is relevant for this junction, represented as a `BodyPart` struct.
239
+ /// @return A `Junction` struct representing the `Plurality` junction with the provided parameters.
240
+ function plurality(
241
+ BodyId memory id,
242
+ BodyPart memory part
243
+ ) pure returns (Junction memory) {
244
+ return
245
+ Junction({
246
+ jType: JunctionType.Plurality,
247
+ payload: abi.encodePacked(
248
+ BodyIdCodec.encode(id),
249
+ BodyPartCodec.encode(part)
250
+ )
251
+ });
252
+ }
253
+
254
+ /// @notice Creates a `GlobalConsensus` junction, which represents a global network capable of externalizing its own consensus.
255
+ /// @return A `Junction` struct representing the `GlobalConsensus` junction, with an empty payload.
256
+ function globalConsensus() pure returns (Junction memory) {
257
+ return Junction({jType: JunctionType.GlobalConsensus, payload: ""});
258
+ }