solidity-scale-codec 0.3.4 → 1.0.1

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 (47) hide show
  1. package/CHANGELOG.md +37 -3
  2. package/README.md +10 -28
  3. package/package.json +2 -2
  4. package/src/Utils/BytesUtils.sol +26 -0
  5. package/src/Utils/UnsignedUtils.sol +52 -0
  6. package/src/Xcm/VersionedXcm/VersionedXcm.sol +4 -1
  7. package/src/Xcm/VersionedXcm/VersionedXcmCodec.sol +11 -0
  8. package/src/Xcm/v3/MaybeErrorCode/MaybeErrorCode.sol +8 -7
  9. package/src/Xcm/v3/MaybeErrorCode/MaybeErrorCodeCodec.sol +18 -19
  10. package/src/Xcm/v5/AssetFilter/AssetFilter.sol +8 -8
  11. package/src/Xcm/v5/AssetFilter/AssetFilterCodec.sol +47 -34
  12. package/src/Xcm/v5/AssetInstance/AssetInstance.sol +13 -12
  13. package/src/Xcm/v5/AssetInstance/AssetInstanceCodec.sol +53 -56
  14. package/src/Xcm/v5/AssetTransferFilter/AssetTransferFilter.sol +12 -12
  15. package/src/Xcm/v5/AssetTransferFilter/AssetTransferFilterCodec.sol +48 -20
  16. package/src/Xcm/v5/Assets/Assets.sol +16 -0
  17. package/src/Xcm/v5/Assets/AssetsCodec.sol +3 -3
  18. package/src/Xcm/v5/BodyId/BodyId.sol +24 -24
  19. package/src/Xcm/v5/BodyId/BodyIdCodec.sol +41 -48
  20. package/src/Xcm/v5/BodyPart/BodyPart.sol +44 -28
  21. package/src/Xcm/v5/BodyPart/BodyPartCodec.sol +70 -37
  22. package/src/Xcm/v5/Constants.sol +2 -2
  23. package/src/Xcm/v5/Fungibility/Fungibility.sol +6 -6
  24. package/src/Xcm/v5/Fungibility/FungibilityCodec.sol +40 -36
  25. package/src/Xcm/v5/Hint/Hint.sol +5 -5
  26. package/src/Xcm/v5/Hint/HintCodec.sol +24 -20
  27. package/src/Xcm/v5/Instruction/Instruction.sol +81 -55
  28. package/src/Xcm/v5/Instruction/InstructionCodec.sol +1047 -73
  29. package/src/Xcm/v5/Junction/Junction.sol +55 -69
  30. package/src/Xcm/v5/Junction/JunctionCodec.sol +72 -135
  31. package/src/Xcm/v5/Junctions/Junctions.sol +34 -0
  32. package/src/Xcm/v5/Junctions/JunctionsCodec.sol +0 -18
  33. package/src/Xcm/v5/Location/Location.sol +8 -0
  34. package/src/Xcm/v5/NetworkId/NetworkId.sol +15 -16
  35. package/src/Xcm/v5/NetworkId/NetworkIdCodec.sol +57 -34
  36. package/src/Xcm/v5/OriginKind/OriginKindCodec.sol +1 -1
  37. package/src/Xcm/v5/Response/Response.sol +49 -40
  38. package/src/Xcm/v5/Response/ResponseCodec.sol +64 -54
  39. package/src/Xcm/v5/Weight/WeightCodec.sol +3 -2
  40. package/src/Xcm/v5/WeightLimit/WeightLimit.sol +6 -6
  41. package/src/Xcm/v5/WeightLimit/WeightLimitCodec.sol +32 -23
  42. package/src/Xcm/v5/WildAsset/WildAsset.sol +17 -25
  43. package/src/Xcm/v5/WildAsset/WildAssetCodec.sol +35 -38
  44. package/src/Xcm/v5/WildFungibility/WildFungibilityCodec.sol +6 -6
  45. package/src/Xcm/v5/Xcm/XcmBuilder.sol +689 -0
  46. package/src/Xcm/v5/XcmError/XcmError.sol +7 -7
  47. package/src/Xcm/v5/XcmError/XcmErrorCodec.sol +25 -22
@@ -12,7 +12,7 @@ import {Address} from "../../../Scale/Address.sol";
12
12
  import {Compact} from "../../../Scale/Compact.sol";
13
13
 
14
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 {
15
+ enum JunctionVariant {
16
16
  /// @custom:variant An indexed parachain belonging to and operated by the context.
17
17
  Parachain,
18
18
  /// @custom:variantA 32-byte identifier for an account of a specific network that is respected as a sovereign endpoint within the context.
@@ -35,6 +35,12 @@ enum JunctionType {
35
35
  GlobalConsensus
36
36
  }
37
37
 
38
+ /// @notice Parameters for a `Parachain` junction.
39
+ struct ParachainParams {
40
+ /// @custom:property The parachain identifier.
41
+ uint32 parachainId;
42
+ }
43
+
38
44
  /// @notice Parameters for an `AccountId32` junction, containing optional network information and a 32-byte account identifier.
39
45
  struct AccountId32Params {
40
46
  /// @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.
@@ -81,12 +87,6 @@ struct GeneralKeyParams {
81
87
  bytes32 key;
82
88
  }
83
89
 
84
- /// @notice Parameters for a `Parachain` junction.
85
- struct ParachainParams {
86
- /// @custom:property The parachain identifier.
87
- uint32 parachainId;
88
- }
89
-
90
90
  /// @notice Parameters for a `PalletInstance` junction.
91
91
  struct PalletInstanceParams {
92
92
  /// @custom:property The pallet instance index.
@@ -101,9 +101,9 @@ struct GeneralIndexParams {
101
101
 
102
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
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`.
104
+ /// @custom:property variant The type of the junction, determining how to interpret the payload. See `JunctionVariant` enum for possible values.
105
+ JunctionVariant variant;
106
+ /// @custom:property payload The SCALE-encoded data specific to the junction type. The structure of this data varies based on `variant`.
107
107
  bytes payload;
108
108
  }
109
109
 
@@ -123,68 +123,52 @@ function parachain(
123
123
  ) pure returns (Junction memory) {
124
124
  return
125
125
  Junction({
126
- jType: JunctionType.Parachain,
126
+ variant: JunctionVariant.Parachain,
127
127
  payload: Compact.encode(params.parachainId)
128
128
  });
129
129
  }
130
130
 
131
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.
132
+ /// @param params Parameters for the account-id-32 variant.
135
133
  /// @return A `Junction` struct representing the `AccountId32` junction with the provided parameters.
136
134
  function accountId32(
137
- bool hasNetwork,
138
- NetworkId memory network,
139
- bytes32 id
135
+ AccountId32Params memory params
140
136
  ) pure returns (Junction memory) {
141
- return
142
- Junction({
143
- jType: JunctionType.AccountId32,
144
- payload: abi.encodePacked(hasNetwork, network.encode(), id.encode())
145
- });
137
+ bytes memory payload = abi.encodePacked(params.hasNetwork);
138
+ if (params.hasNetwork) {
139
+ payload = abi.encodePacked(payload, params.network.encode());
140
+ }
141
+ payload = abi.encodePacked(payload, params.id);
142
+ return Junction({variant: JunctionVariant.AccountId32, payload: payload});
146
143
  }
147
144
 
148
145
  /// @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.
146
+ /// @param params Parameters for the account-index-64 variant.
152
147
  /// @return A `Junction` struct representing the `AccountIndex64` junction with the provided parameters.
153
148
  function accountIndex64(
154
- bool hasNetwork,
155
- NetworkId memory network,
156
- uint64 index
149
+ AccountIndex64Params memory params
157
150
  ) pure returns (Junction memory) {
151
+ bytes memory payload = abi.encodePacked(params.hasNetwork);
152
+ if (params.hasNetwork) {
153
+ payload = abi.encodePacked(payload, params.network.encode());
154
+ }
155
+ payload = abi.encodePacked(payload, Compact.encode(params.index));
158
156
  return
159
- Junction({
160
- jType: JunctionType.AccountIndex64,
161
- payload: abi.encodePacked(
162
- hasNetwork,
163
- network.encode(),
164
- Compact.encode(index)
165
- )
166
- });
157
+ Junction({variant: JunctionVariant.AccountIndex64, payload: payload});
167
158
  }
168
159
 
169
160
  /// @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.
161
+ /// @param params Parameters for the account-key-20 variant.
173
162
  /// @return A `Junction` struct representing the `AccountKey20` junction with the provided parameters.
174
163
  function accountKey20(
175
- bool hasNetwork,
176
- NetworkId memory network,
177
- address key
164
+ AccountKey20Params memory params
178
165
  ) 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
- });
166
+ bytes memory payload = abi.encodePacked(params.hasNetwork);
167
+ if (params.hasNetwork) {
168
+ payload = abi.encodePacked(payload, params.network.encode());
169
+ }
170
+ payload = abi.encodePacked(payload, params.key.encode());
171
+ return Junction({variant: JunctionVariant.AccountKey20, payload: payload});
188
172
  }
189
173
 
190
174
  /// @notice Creates a `PalletInstance` junction with the given instance index.
@@ -195,7 +179,7 @@ function palletInstance(
195
179
  ) pure returns (Junction memory) {
196
180
  return
197
181
  Junction({
198
- jType: JunctionType.PalletInstance,
182
+ variant: JunctionVariant.PalletInstance,
199
183
  payload: abi.encodePacked(params.instance)
200
184
  });
201
185
  }
@@ -208,45 +192,47 @@ function generalIndex(
208
192
  ) pure returns (Junction memory) {
209
193
  return
210
194
  Junction({
211
- jType: JunctionType.GeneralIndex,
195
+ variant: JunctionVariant.GeneralIndex,
212
196
  payload: Compact.encode(params.index)
213
197
  });
214
198
  }
215
199
 
216
200
  /// @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.
201
+ /// @param params Parameters for the general-key variant.
219
202
  /// @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();
203
+ function generalKey(
204
+ GeneralKeyParams memory params
205
+ ) pure returns (Junction memory) {
206
+ if (
207
+ params.length == 0 ||
208
+ params.length > 32 ||
209
+ params.key.length != params.length
210
+ ) revert InvalidJunctionPayload();
223
211
  return
224
212
  Junction({
225
- jType: JunctionType.GeneralKey,
226
- payload: abi.encodePacked(length, key)
213
+ variant: JunctionVariant.GeneralKey,
214
+ payload: abi.encodePacked(params.length, params.key)
227
215
  });
228
216
  }
229
217
 
230
218
  /// @notice Creates an `OnlyChild` junction, which represents the unambiguous child in the context.
231
219
  /// @return A `Junction` struct representing the `OnlyChild` junction, with an empty payload.
232
220
  function onlyChild() pure returns (Junction memory) {
233
- return Junction({jType: JunctionType.OnlyChild, payload: ""});
221
+ return Junction({variant: JunctionVariant.OnlyChild, payload: ""});
234
222
  }
235
223
 
236
224
  /// @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.
225
+ /// @param params Parameters for the plurality variant.
239
226
  /// @return A `Junction` struct representing the `Plurality` junction with the provided parameters.
240
227
  function plurality(
241
- BodyId memory id,
242
- BodyPart memory part
228
+ PluralityParams memory params
243
229
  ) pure returns (Junction memory) {
244
230
  return
245
231
  Junction({
246
- jType: JunctionType.Plurality,
232
+ variant: JunctionVariant.Plurality,
247
233
  payload: abi.encodePacked(
248
- BodyIdCodec.encode(id),
249
- BodyPartCodec.encode(part)
234
+ BodyIdCodec.encode(params.id),
235
+ BodyPartCodec.encode(params.part)
250
236
  )
251
237
  });
252
238
  }
@@ -254,5 +240,5 @@ function plurality(
254
240
  /// @notice Creates a `GlobalConsensus` junction, which represents a global network capable of externalizing its own consensus.
255
241
  /// @return A `Junction` struct representing the `GlobalConsensus` junction, with an empty payload.
256
242
  function globalConsensus() pure returns (Junction memory) {
257
- return Junction({jType: JunctionType.GlobalConsensus, payload: ""});
243
+ return Junction({variant: JunctionVariant.GlobalConsensus, payload: ""});
258
244
  }
@@ -12,13 +12,18 @@ import {Address} from "../../../Scale/Address.sol";
12
12
  import {Compact} from "../../../Scale/Compact.sol";
13
13
  import {
14
14
  Junction,
15
- JunctionType,
15
+ JunctionVariant,
16
+ ParachainParams,
16
17
  AccountId32Params,
17
18
  PluralityParams,
18
19
  AccountIndex64Params,
19
20
  AccountKey20Params,
20
- GeneralKeyParams
21
+ GeneralKeyParams,
22
+ PalletInstanceParams,
23
+ GeneralIndexParams
21
24
  } from "./Junction.sol";
25
+ import {BytesUtils} from "../../../Utils/BytesUtils.sol";
26
+ import {UnsignedUtils} from "../../../Utils/UnsignedUtils.sol";
22
27
 
23
28
  /// @title SCALE Codec for XCM v5 `Junction`
24
29
  /// @notice SCALE-compliant encoder/decoder for the `Junction` type.
@@ -26,7 +31,7 @@ import {
26
31
  /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/index.html
27
32
  library JunctionCodec {
28
33
  error InvalidJunctionLength();
29
- error InvalidJunctionType(uint8 jType);
34
+ error InvalidJunctionVariant(uint8 variant);
30
35
  error InvalidJunctionPayload();
31
36
 
32
37
  /// @notice Encodes a `Junction` struct into a byte array suitable for SCALE encoding.
@@ -35,7 +40,7 @@ library JunctionCodec {
35
40
  function encode(
36
41
  Junction memory junction
37
42
  ) internal pure returns (bytes memory) {
38
- return abi.encodePacked(uint8(junction.jType), junction.payload);
43
+ return abi.encodePacked(uint8(junction.variant), junction.payload);
39
44
  }
40
45
 
41
46
  /// @notice Returns the number of bytes that a `Junction` struct would occupy when SCALE-encoded, starting from the specified offset.
@@ -47,44 +52,41 @@ library JunctionCodec {
47
52
  uint256 offset
48
53
  ) internal pure returns (uint256) {
49
54
  if (offset >= data.length) revert InvalidJunctionLength();
50
- uint8 jType;
51
- assembly {
52
- jType := shr(248, mload(add(add(data, 32), offset)))
53
- }
55
+ uint8 variant = uint8(data[offset]);
54
56
  uint256 payloadLength;
55
57
  ++offset; // Move past the type byte
56
- if (jType == uint8(JunctionType.Parachain)) {
58
+ if (variant == uint8(JunctionVariant.Parachain)) {
57
59
  payloadLength = Compact.encodedSizeAt(data, offset);
58
- } else if (jType == uint8(JunctionType.AccountId32)) {
60
+ } else if (variant == uint8(JunctionVariant.AccountId32)) {
59
61
  payloadLength = _innerNetworkIdSize(data, offset) + 32; // for the account ID;
60
- } else if (jType == uint8(JunctionType.AccountIndex64)) {
62
+ } else if (variant == uint8(JunctionVariant.AccountIndex64)) {
61
63
  payloadLength = _innerNetworkIdSize(data, offset);
62
64
  payloadLength += Compact.encodedSizeAt(
63
65
  data,
64
66
  offset + payloadLength
65
67
  ); // for the account index
66
- } else if (jType == uint8(JunctionType.AccountKey20)) {
68
+ } else if (variant == uint8(JunctionVariant.AccountKey20)) {
67
69
  payloadLength = _innerNetworkIdSize(data, offset) + 20; // for the account key;
68
- } else if (jType == uint8(JunctionType.PalletInstance)) {
70
+ } else if (variant == uint8(JunctionVariant.PalletInstance)) {
69
71
  payloadLength = 1;
70
- } else if (jType == uint8(JunctionType.GeneralIndex)) {
72
+ } else if (variant == uint8(JunctionVariant.GeneralIndex)) {
71
73
  payloadLength = Compact.encodedSizeAt(data, offset);
72
- } else if (jType == uint8(JunctionType.GeneralKey)) {
74
+ } else if (variant == uint8(JunctionVariant.GeneralKey)) {
73
75
  if (offset >= data.length) revert InvalidJunctionLength();
74
76
  uint8 length = uint8(data[offset]);
75
77
  payloadLength = 1 + length; // 1 byte for the length + the key bytes
76
78
  } else if (
77
- jType == uint8(JunctionType.OnlyChild) ||
78
- jType == uint8(JunctionType.GlobalConsensus)
79
+ variant == uint8(JunctionVariant.OnlyChild) ||
80
+ variant == uint8(JunctionVariant.GlobalConsensus)
79
81
  ) {
80
82
  payloadLength = 0;
81
- } else if (jType == uint8(JunctionType.Plurality)) {
83
+ } else if (variant == uint8(JunctionVariant.Plurality)) {
82
84
  uint256 innerLength = BodyIdCodec.encodedSizeAt(data, offset);
83
85
  payloadLength =
84
86
  innerLength +
85
87
  BodyPartCodec.encodedSizeAt(data, offset + innerLength);
86
88
  } else {
87
- revert InvalidJunctionType(jType);
89
+ revert InvalidJunctionVariant(variant);
88
90
  }
89
91
 
90
92
  return 1 + payloadLength; // 1 byte for the type + payload length
@@ -109,36 +111,25 @@ library JunctionCodec {
109
111
  bytes memory data,
110
112
  uint256 offset
111
113
  ) internal pure returns (Junction memory junction, uint256 bytesRead) {
112
- if (offset >= data.length) revert InvalidJunctionLength();
113
- uint8 jType;
114
- assembly {
115
- jType := shr(248, mload(add(add(data, 32), offset)))
116
- }
117
114
  uint256 payloadLength = encodedSizeAt(data, offset) - 1; // Subtract 1 byte for the type
118
- bytes memory payload = new bytes(payloadLength);
119
- for (uint256 i = 0; i < payloadLength; i++) {
120
- payload[i] = data[offset + 1 + i];
121
- }
122
- junction = Junction({jType: JunctionType(jType), payload: payload});
115
+ uint8 variant = uint8(data[offset]);
116
+ bytes memory payload = BytesUtils.copy(data, offset + 1, payloadLength);
117
+ junction = Junction({
118
+ variant: JunctionVariant(variant),
119
+ payload: payload
120
+ });
123
121
  bytesRead = 1 + payload.length;
124
122
  }
125
123
 
126
124
  /// @notice Decodes a `Parachain` junction from a given `Junction` struct, extracting the parachain ID.
127
125
  /// @param junction The `Junction` struct to decode, which should represent a `Parachain` junction.
128
- /// @return parachainId The ID of the parachain extracted from the junction's payload.
126
+ /// @return params A `ParachainParams` struct containing the decoded parachain ID.
129
127
  function asParachain(
130
128
  Junction memory junction
131
- ) internal pure returns (uint32 parachainId) {
132
- if (junction.jType != JunctionType.Parachain)
133
- revert InvalidJunctionType(uint8(junction.jType));
134
- if (junction.payload.length != 4) revert InvalidJunctionPayload();
129
+ ) internal pure returns (ParachainParams memory params) {
130
+ _assertVariant(junction, JunctionVariant.Parachain);
135
131
  (uint256 decodedParachain, ) = Compact.decode(junction.payload);
136
- if (decodedParachain > type(uint32).max) {
137
- revert InvalidJunctionPayload();
138
- }
139
- unchecked {
140
- parachainId = uint32(decodedParachain);
141
- }
132
+ params.parachainId = UnsignedUtils.toU32(decodedParachain);
142
133
  }
143
134
 
144
135
  /// @notice Decodes an `AccountId32` junction from a given `Junction` struct, extracting the network information and account ID.
@@ -147,28 +138,18 @@ library JunctionCodec {
147
138
  function asAccountId32(
148
139
  Junction memory junction
149
140
  ) internal pure returns (AccountId32Params memory params) {
150
- if (junction.jType != JunctionType.AccountId32)
151
- revert InvalidJunctionType(uint8(junction.jType));
152
- if (junction.payload.length != 33 && junction.payload.length != 34)
153
- revert InvalidJunctionPayload();
154
- bool hasNetwork = junction.payload[0] != 0;
141
+ _assertVariant(junction, JunctionVariant.AccountId32);
142
+ params.hasNetwork = junction.payload[0] != 0;
155
143
  uint256 offset = 1;
156
- NetworkId memory network;
157
144
  uint256 bytesRead;
158
- if (hasNetwork) {
159
- (network, bytesRead) = NetworkIdCodec.decodeAt(
145
+ if (params.hasNetwork) {
146
+ (params.network, bytesRead) = NetworkIdCodec.decodeAt(
160
147
  junction.payload,
161
148
  offset
162
149
  );
163
150
  offset += bytesRead;
164
151
  }
165
- bytes32 id = Bytes32.decodeAt(junction.payload, offset);
166
- return
167
- AccountId32Params({
168
- hasNetwork: hasNetwork,
169
- network: network,
170
- id: id
171
- });
152
+ params.id = Bytes32.decodeAt(junction.payload, offset);
172
153
  }
173
154
 
174
155
  /// @notice Decodes an `AccountIndex64` junction from a given `Junction` struct, extracting the network information and account index.
@@ -177,35 +158,19 @@ library JunctionCodec {
177
158
  function asAccountIndex64(
178
159
  Junction memory junction
179
160
  ) internal pure returns (AccountIndex64Params memory params) {
180
- if (junction.jType != JunctionType.AccountIndex64)
181
- revert InvalidJunctionType(uint8(junction.jType));
182
- if (junction.payload.length != 9 && junction.payload.length != 10)
183
- revert InvalidJunctionPayload();
184
- bool hasNetwork = junction.payload[0] != 0;
161
+ _assertVariant(junction, JunctionVariant.AccountIndex64);
162
+ params.hasNetwork = junction.payload[0] != 0;
185
163
  uint256 offset = 1;
186
- NetworkId memory network;
187
164
  uint256 bytesRead;
188
- if (hasNetwork) {
189
- (network, bytesRead) = NetworkIdCodec.decodeAt(
165
+ if (params.hasNetwork) {
166
+ (params.network, bytesRead) = NetworkIdCodec.decodeAt(
190
167
  junction.payload,
191
168
  offset
192
169
  );
193
170
  offset += bytesRead;
194
171
  }
195
172
  (uint256 decodedIndex, ) = Compact.decodeAt(junction.payload, offset);
196
- if (decodedIndex > type(uint64).max) {
197
- revert InvalidJunctionPayload();
198
- }
199
- uint64 index;
200
- unchecked {
201
- index = uint64(decodedIndex);
202
- }
203
- return
204
- AccountIndex64Params({
205
- hasNetwork: hasNetwork,
206
- network: network,
207
- index: index
208
- });
173
+ params.index = UnsignedUtils.toU64(decodedIndex);
209
174
  }
210
175
 
211
176
  /// @notice Decodes an `AccountKey20` junction from a given `Junction` struct, extracting the network information and account key.
@@ -214,58 +179,39 @@ library JunctionCodec {
214
179
  function asAccountKey20(
215
180
  Junction memory junction
216
181
  ) internal pure returns (AccountKey20Params memory params) {
217
- if (junction.jType != JunctionType.AccountKey20)
218
- revert InvalidJunctionType(uint8(junction.jType));
219
- if (junction.payload.length != 21 && junction.payload.length != 22)
220
- revert InvalidJunctionPayload();
221
- bool hasNetwork = junction.payload[0] != 0;
182
+ _assertVariant(junction, JunctionVariant.AccountKey20);
183
+ params.hasNetwork = junction.payload[0] != 0;
222
184
  uint256 offset = 1;
223
- NetworkId memory network;
224
185
  uint256 bytesRead;
225
- if (hasNetwork) {
226
- (network, bytesRead) = NetworkIdCodec.decodeAt(
186
+ if (params.hasNetwork) {
187
+ (params.network, bytesRead) = NetworkIdCodec.decodeAt(
227
188
  junction.payload,
228
189
  offset
229
190
  );
230
191
  offset += bytesRead;
231
192
  }
232
- address key = Address.decodeAt(junction.payload, offset);
233
- return
234
- AccountKey20Params({
235
- hasNetwork: hasNetwork,
236
- network: network,
237
- key: key
238
- });
193
+ params.key = Address.decodeAt(junction.payload, offset);
239
194
  }
240
195
 
241
196
  /// @notice Decodes a `PalletInstance` junction from a given `Junction` struct, extracting the pallet instance index.
242
197
  /// @param junction The `Junction` struct to decode, which should represent a `PalletInstance` junction.
243
- /// @return instance The index of the pallet instance extracted from the junction's payload.
198
+ /// @return params A `PalletInstanceParams` struct containing the decoded pallet instance index.
244
199
  function asPalletInstance(
245
200
  Junction memory junction
246
- ) internal pure returns (uint8 instance) {
247
- if (junction.jType != JunctionType.PalletInstance)
248
- revert InvalidJunctionType(uint8(junction.jType));
249
- if (junction.payload.length != 1) revert InvalidJunctionPayload();
250
- return uint8(junction.payload[0]);
201
+ ) internal pure returns (PalletInstanceParams memory params) {
202
+ _assertVariant(junction, JunctionVariant.PalletInstance);
203
+ params.instance = uint8(junction.payload[0]);
251
204
  }
252
205
 
253
206
  /// @notice Decodes a `GeneralIndex` junction from a given `Junction` struct, extracting the general index.
254
207
  /// @param junction The `Junction` struct to decode, which should represent a `GeneralIndex` junction.
255
- /// @return index The general index extracted from the junction's payload.
208
+ /// @return params A `GeneralIndexParams` struct containing the decoded general index.
256
209
  function asGeneralIndex(
257
210
  Junction memory junction
258
- ) internal pure returns (uint128 index) {
259
- if (junction.jType != JunctionType.GeneralIndex)
260
- revert InvalidJunctionType(uint8(junction.jType));
261
- if (junction.payload.length == 0) revert InvalidJunctionPayload();
211
+ ) internal pure returns (GeneralIndexParams memory params) {
212
+ _assertVariant(junction, JunctionVariant.GeneralIndex);
262
213
  (uint256 decodedIndex, ) = Compact.decode(junction.payload);
263
- if (decodedIndex > type(uint128).max) {
264
- revert InvalidJunctionPayload();
265
- }
266
- unchecked {
267
- index = uint128(decodedIndex);
268
- }
214
+ params.index = UnsignedUtils.toU128(decodedIndex);
269
215
  }
270
216
 
271
217
  /// @notice Decodes a `GeneralKey` junction from a given `Junction` struct, extracting the key.
@@ -274,14 +220,9 @@ library JunctionCodec {
274
220
  function asGeneralKey(
275
221
  Junction memory junction
276
222
  ) internal pure returns (GeneralKeyParams memory params) {
277
- if (junction.jType != JunctionType.GeneralKey)
278
- revert InvalidJunctionType(uint8(junction.jType));
279
- if (junction.payload.length == 0) revert InvalidJunctionPayload();
280
- uint8 length = uint8(junction.payload[0]);
281
- if (length == 0 || length > 32 || junction.payload.length != length + 1)
282
- revert InvalidJunctionPayload();
283
- bytes32 key = Bytes32.decodeAt(junction.payload, 1);
284
- return GeneralKeyParams({length: length, key: key});
223
+ _assertVariant(junction, JunctionVariant.GeneralKey);
224
+ params.length = uint8(junction.payload[0]);
225
+ params.key = Bytes32.decodeAt(junction.payload, 1);
285
226
  }
286
227
 
287
228
  /// @notice Decodes a `Plurality` junction from a given `Junction` struct, extracting the body ID and body part.
@@ -290,28 +231,15 @@ library JunctionCodec {
290
231
  function asPlurality(
291
232
  Junction memory junction
292
233
  ) internal pure returns (PluralityParams memory params) {
293
- if (junction.jType != JunctionType.Plurality)
294
- revert InvalidJunctionType(uint8(junction.jType));
295
- if (junction.payload.length == 0) revert InvalidJunctionPayload();
234
+ _assertVariant(junction, JunctionVariant.Plurality);
296
235
  uint256 offset = 0;
297
- BodyId memory id;
298
236
  uint256 bytesRead;
299
- (id, bytesRead) = BodyIdCodec.decodeAt(junction.payload, offset);
237
+ (params.id, bytesRead) = BodyIdCodec.decodeAt(junction.payload, offset);
300
238
  offset += bytesRead;
301
- BodyPart memory part;
302
- (part, bytesRead) = BodyPartCodec.decodeAt(junction.payload, offset);
303
- return PluralityParams({id: id, part: part});
304
- }
305
-
306
- /// @notice Decodes an `GlobalConsensus` junction from a given `Junction` struct and extracts the `NetworkId`.
307
- /// @param junction The `Junction` struct to decode, which should represent a `GlobalConsensus` junction.
308
- /// @return networkId The `NetworkId` extracted from the junction's payload, representing the global network's consensus.
309
- function asGlobalConsensus(
310
- Junction memory junction
311
- ) internal pure returns (NetworkId memory networkId) {
312
- if (junction.jType != JunctionType.GlobalConsensus)
313
- revert InvalidJunctionType(uint8(junction.jType));
314
- (networkId, ) = NetworkIdCodec.decode(junction.payload);
239
+ (params.part, bytesRead) = BodyPartCodec.decodeAt(
240
+ junction.payload,
241
+ offset
242
+ );
315
243
  }
316
244
 
317
245
  function _innerNetworkIdSize(
@@ -326,4 +254,13 @@ library JunctionCodec {
326
254
  }
327
255
  return size;
328
256
  }
257
+
258
+ function _assertVariant(
259
+ Junction memory junction,
260
+ JunctionVariant expected
261
+ ) private pure {
262
+ if (junction.variant != expected) {
263
+ revert InvalidJunctionVariant(uint8(junction.variant));
264
+ }
265
+ }
329
266
  }
@@ -10,3 +10,37 @@ struct Junctions {
10
10
  /// @custom:property The actual junction data
11
11
  Junction[] items;
12
12
  }
13
+
14
+ error InvalidJunctionsCount(uint8 count);
15
+
16
+ /// @notice Creates a `Here` junctions struct.
17
+ /// @return A `Junctions` struct representing the `Here` variant.
18
+ function here() pure returns (Junctions memory) {
19
+ return Junctions({count: 0, items: new Junction[](0)});
20
+ }
21
+
22
+ /// @notice Creates a `Junctions` struct from a single `Junction`, representing the `X1` variant.
23
+ /// @param junction The `Junction` to include in the `Junctions`.
24
+ /// @return A `Junctions` struct containing the provided `Junction`.
25
+ function fromJunction(
26
+ Junction memory junction
27
+ ) pure returns (Junctions memory) {
28
+ Junction[] memory js = new Junction[](1);
29
+ js[0] = junction;
30
+ return Junctions({count: 1, items: js});
31
+ }
32
+
33
+ /// @notice Creates a `Junctions` struct with the given junctions.
34
+ /// @param junctions An array of `Junction` structs to include in the `Junctions`.
35
+ /// @return A `Junctions` struct containing the provided junctions.
36
+ function fromJunctionArr(
37
+ Junction[] memory junctions
38
+ ) pure returns (Junctions memory) {
39
+ if (junctions.length == 0) {
40
+ return here();
41
+ }
42
+ if (junctions.length > 8) {
43
+ revert InvalidJunctionsCount(uint8(junctions.length));
44
+ }
45
+ return Junctions({count: uint8(junctions.length), items: junctions});
46
+ }
@@ -13,24 +13,6 @@ library JunctionsCodec {
13
13
  error InvalidJunctionsLength(uint8 count);
14
14
  error InvalidJunctionsCount(uint8 count);
15
15
 
16
- /// @notice Creates a `Here` junctions struct.
17
- function here() internal pure returns (Junctions memory) {
18
- return Junctions({count: 0, items: new Junction[](0)});
19
- }
20
-
21
- /// @notice Creates a `Junctions` struct with the given junctions.
22
- function junction(
23
- Junction[] memory junctions
24
- ) internal pure returns (Junctions memory) {
25
- if (junctions.length == 0) {
26
- return here();
27
- }
28
- if (junctions.length > 8) {
29
- revert InvalidJunctionsCount(uint8(junctions.length));
30
- }
31
- return Junctions({count: uint8(junctions.length), items: junctions});
32
- }
33
-
34
16
  /// @notice Encodes a Junctions struct into bytes.
35
17
  /// @param junctions The Junctions struct to encode.
36
18
  /// @return SCALE-encoded byte sequence representing the Junctions.
@@ -3,6 +3,8 @@ pragma solidity ^0.8.28;
3
3
 
4
4
  import {Junctions} from "../Junctions/Junctions.sol";
5
5
 
6
+ import {here} from "../Junctions/Junctions.sol";
7
+
6
8
  /// @notice A relative path between state-bearing consensus systems.
7
9
  struct Location {
8
10
  /// @custom:property The number of parent junctions at the beginning of this Location.
@@ -10,3 +12,9 @@ struct Location {
10
12
  /// @custom:property The interior (i.e. non-parent) junctions that this Location contains. See `Junctions` struct for details.
11
13
  Junctions interior;
12
14
  }
15
+
16
+ /// @notice Creates a `Location` struct representing the parent location (i.e., one level up in the hierarchy).
17
+ /// @return A `Location` struct with `parents` set to 1 and an empty `interior`.
18
+ function parent() pure returns (Location memory) {
19
+ return Location({parents: 1, interior: here()});
20
+ }