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,128 @@
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
+ import {BodyId, BodyIdType} from "./BodyId.sol";
7
+
8
+ /// @title SCALE Codec for XCM v5 `BodyId`
9
+ /// @notice SCALE-compliant encoder/decoder for the `BodyId` type.
10
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
11
+ /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/index.html
12
+ library BodyIdCodec {
13
+ error InvalidBodyIdLength();
14
+ error InvalidBodyIdType(uint8 bodyIdType);
15
+
16
+ /// @notice Encodes a `BodyId` into bytes.
17
+ /// @param bodyId The `BodyId` to encode.
18
+ /// @return SCALE-encoded byte sequence representing the `BodyId`.
19
+ function encode(BodyId memory bodyId) internal pure returns (bytes memory) {
20
+ return abi.encodePacked(uint8(bodyId.bodyIdType), bodyId.payload);
21
+ }
22
+
23
+ /// @notice Returns the number of bytes that a `BodyId` struct would occupy when SCALE-encoded, starting at a given offset in the data.
24
+ /// @param data The byte sequence containing the encoded `BodyId`.
25
+ /// @param offset The starting index in `data` from which to calculate the encoded size of the `BodyId`.
26
+ /// @return The number of bytes that the `BodyId` struct would occupy when SCALE-encoded.
27
+ function encodedSizeAt(
28
+ bytes memory data,
29
+ uint256 offset
30
+ ) internal pure returns (uint256) {
31
+ if (data.length < offset + 1) {
32
+ revert InvalidBodyIdLength();
33
+ }
34
+ uint8 bodyIdTypeValue = uint8(data[offset]);
35
+ BodyIdType bodyIdType = BodyIdType(bodyIdTypeValue);
36
+ uint256 payloadLength;
37
+ if (
38
+ bodyIdType == BodyIdType.Unit ||
39
+ bodyIdType == BodyIdType.Executive ||
40
+ bodyIdType == BodyIdType.Technical ||
41
+ bodyIdType == BodyIdType.Legislative ||
42
+ bodyIdType == BodyIdType.Judicial ||
43
+ bodyIdType == BodyIdType.Defense ||
44
+ bodyIdType == BodyIdType.Administration ||
45
+ bodyIdType == BodyIdType.Treasury
46
+ ) {
47
+ payloadLength = 0;
48
+ } else if (bodyIdType == BodyIdType.Moniker) {
49
+ payloadLength = 4;
50
+ } else if (bodyIdType == BodyIdType.Index) {
51
+ payloadLength = 4;
52
+ } else {
53
+ revert InvalidBodyIdType(bodyIdTypeValue);
54
+ }
55
+
56
+ if (data.length < offset + 1 + payloadLength) {
57
+ revert InvalidBodyIdLength();
58
+ }
59
+
60
+ return 1 + payloadLength;
61
+ }
62
+
63
+ /// @notice Decodes a `BodyId` from bytes starting at the beginning of the data.
64
+ /// @param data The byte sequence containing the encoded `BodyId`.
65
+ /// @return bodyId The decoded `BodyId`.
66
+ /// @return bytesRead The total number of bytes read from the input data to decode the `BodyId`.
67
+ function decode(
68
+ bytes memory data
69
+ ) internal pure returns (BodyId memory bodyId, uint256 bytesRead) {
70
+ return decodeAt(data, 0);
71
+ }
72
+
73
+ /// @notice Decodes a `BodyId` from bytes starting at a given offset.
74
+ /// @param data The byte sequence containing the encoded `BodyId`.
75
+ /// @param offset The starting index in `data` from which to decode the `BodyId`.
76
+ /// @return bodyId The decoded `BodyId`.
77
+ /// @return bytesRead The total number of bytes read from the input data to decode the `BodyId`.
78
+ function decodeAt(
79
+ bytes memory data,
80
+ uint256 offset
81
+ ) internal pure returns (BodyId memory bodyId, uint256 bytesRead) {
82
+ if (data.length < offset + 1) {
83
+ revert InvalidBodyIdLength();
84
+ }
85
+ uint8 bodyIdTypeValue = uint8(data[offset]);
86
+ BodyIdType bodyIdType = BodyIdType(bodyIdTypeValue);
87
+ uint256 payloadLength = encodedSizeAt(data, offset) - 1; // subtract 1 byte for the bodyIdType
88
+ bytes memory payload = new bytes(payloadLength);
89
+
90
+ for (uint256 i = 0; i < payloadLength; i++) {
91
+ payload[i] = data[offset + 1 + i];
92
+ }
93
+
94
+ bodyId = BodyId({bodyIdType: bodyIdType, payload: payload});
95
+ bytesRead = 1 + payloadLength;
96
+ }
97
+
98
+ /// @notice Helper function to decode a `BodyId` and extract the moniker name if the type is `Moniker`.
99
+ /// @param bodyId The `BodyId` to extract the moniker name from.
100
+ /// @return name The 4-byte name of the moniker if the `bodyIdType` is `Moniker`.
101
+ function asMoniker(
102
+ BodyId memory bodyId
103
+ ) internal pure returns (bytes4 name) {
104
+ if (bodyId.bodyIdType != BodyIdType.Moniker) {
105
+ revert InvalidBodyIdType(uint8(bodyId.bodyIdType));
106
+ }
107
+ return Bytes4.decode(bodyId.payload);
108
+ }
109
+
110
+ /// @notice Helper function to decode a `BodyId` and extract the index if the type is `Index`.
111
+ /// @param bodyId The `BodyId` to extract the index from.
112
+ /// @return idx The index of the body if the `bodyIdType` is `Index`.
113
+ function asIndex(BodyId memory bodyId) internal pure returns (uint32 idx) {
114
+ if (bodyId.bodyIdType != BodyIdType.Index) {
115
+ revert InvalidBodyIdType(uint8(bodyId.bodyIdType));
116
+ }
117
+ if (bodyId.payload.length != 4) {
118
+ revert InvalidBodyIdLength();
119
+ }
120
+ (uint256 decodedIndex, ) = Compact.decode(bodyId.payload);
121
+ if (decodedIndex > type(uint32).max) {
122
+ revert InvalidBodyIdLength();
123
+ }
124
+ unchecked {
125
+ idx = uint32(decodedIndex);
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,105 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Compact} from "../../../Scale/Compact.sol";
5
+
6
+ /// @dev Discriminant for the different types of BodyParts in XCM v5. Each variant corresponds to a specific structure of the payload.
7
+ enum BodyPartType {
8
+ /// @custom:variant The body's declaration, under whatever means it decides.
9
+ Voice,
10
+ /// @custom:variant A given number of members of the body.
11
+ Members,
12
+ /// @custom:variant A given number of members of the body, out of some larger caucus.
13
+ Fraction,
14
+ /// @custom:variant No less than the given proportion of members of the body.
15
+ AtLeastProportion,
16
+ /// @custom:variant More than the given proportion of members of the body.
17
+ MoreThanProportion
18
+ }
19
+
20
+ /// @notice A part of a pluralistic body.
21
+ struct BodyPart {
22
+ /// @custom:property The type of BodyPart, which determines how to interpret the payload. See `BodyPartType` enum for possible values.
23
+ BodyPartType bodyPartType;
24
+ /// @custom:property For Members, this will hold the count. For Fraction, AtLeastProportion, and MoreThanProportion, this will hold the encoded Proportion struct. For Voice, this will be empty.
25
+ bytes payload;
26
+ }
27
+
28
+ /// @notice Parameters for the `Members` variant.
29
+ struct MembersParams {
30
+ /// @custom:property The number of members in the body part.
31
+ uint32 count;
32
+ }
33
+
34
+ // ============ Factory Functions ============
35
+
36
+ /// @notice Creates a `BodyPart` struct representing a `Voice` body part.
37
+ /// @return A `BodyPart` with the `Voice` variant.
38
+ function voice() pure returns (BodyPart memory) {
39
+ return BodyPart({bodyPartType: BodyPartType.Voice, payload: ""});
40
+ }
41
+
42
+ /// @notice Creates a `BodyPart` struct representing a `Members` body part with the given count.
43
+ /// @param params Parameters for the members variant.
44
+ /// @return A `BodyPart` with the `Members` variant and the count encoded in the payload.
45
+ function members(MembersParams memory params) pure returns (BodyPart memory) {
46
+ return
47
+ BodyPart({
48
+ bodyPartType: BodyPartType.Members,
49
+ payload: Compact.encode(params.count)
50
+ });
51
+ }
52
+
53
+ /// @notice Creates a `BodyPart` struct representing a `Fraction` body part with the given proportion.
54
+ /// @param nominator The numerator of the proportion, representing the number of members in favor.
55
+ /// @param denominator The denominator of the proportion, representing the total number of members considered.
56
+ /// @return A `BodyPart` with the `Fraction` variant and the encoded proportion in the payload.
57
+ function fraction(
58
+ uint32 nominator,
59
+ uint32 denominator
60
+ ) pure returns (BodyPart memory) {
61
+ return
62
+ BodyPart({
63
+ bodyPartType: BodyPartType.Fraction,
64
+ payload: abi.encodePacked(
65
+ Compact.encode(nominator),
66
+ Compact.encode(denominator)
67
+ )
68
+ });
69
+ }
70
+
71
+ /// @notice Creates a `BodyPart` struct representing an `AtLeastProportion` body part with the given proportion.
72
+ /// @param nominator The numerator of the proportion, representing the minimum number of members in favor.
73
+ /// @param denominator The denominator of the proportion, representing the total number of members considered.
74
+ /// @return A `BodyPart` with the `AtLeastProportion` variant and the encoded proportion in the payload.
75
+ function atLeastProportion(
76
+ uint32 nominator,
77
+ uint32 denominator
78
+ ) pure returns (BodyPart memory) {
79
+ return
80
+ BodyPart({
81
+ bodyPartType: BodyPartType.AtLeastProportion,
82
+ payload: abi.encodePacked(
83
+ Compact.encode(nominator),
84
+ Compact.encode(denominator)
85
+ )
86
+ });
87
+ }
88
+
89
+ /// @notice Creates a `BodyPart` struct representing a `MoreThanProportion` body part with the given proportion.
90
+ /// @param nominator The numerator of the proportion, representing the minimum number of members in favor.
91
+ /// @param denominator The denominator of the proportion, representing the total number of members considered.
92
+ /// @return A `BodyPart` with the `MoreThanProportion` variant and the encoded proportion in the payload.
93
+ function moreThanProportion(
94
+ uint32 nominator,
95
+ uint32 denominator
96
+ ) pure returns (BodyPart memory) {
97
+ return
98
+ BodyPart({
99
+ bodyPartType: BodyPartType.MoreThanProportion,
100
+ payload: abi.encodePacked(
101
+ Compact.encode(nominator),
102
+ Compact.encode(denominator)
103
+ )
104
+ });
105
+ }
@@ -0,0 +1,134 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Compact} from "../../../Scale/Compact.sol";
5
+ import {BodyPart, BodyPartType} from "./BodyPart.sol";
6
+
7
+ /// @title SCALE Codec for XCM v5 `BodyPart`
8
+ /// @notice SCALE-compliant encoder/decoder for the `BodyPart` type.
9
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
10
+ /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/index.html
11
+ library BodyPartCodec {
12
+ error InvalidBodyPartLength();
13
+ error InvalidBodyPartType(uint8 bodyPartType);
14
+ error InvalidBodyPartPayload();
15
+
16
+ /// @notice Encodes a `BodyPart` struct into bytes.
17
+ /// @param bodyPart The `BodyPart` struct to encode.
18
+ /// @return SCALE-encoded byte sequence representing the `BodyPart`.
19
+ function encode(
20
+ BodyPart memory bodyPart
21
+ ) internal pure returns (bytes memory) {
22
+ return abi.encodePacked(uint8(bodyPart.bodyPartType), bodyPart.payload);
23
+ }
24
+
25
+ /// @notice Returns the number of bytes that a `BodyPart` struct would occupy when SCALE-encoded.
26
+ /// @param data The byte sequence containing the encoded `BodyPart`.
27
+ /// @param offset The starting index in `data` from which to calculate the encoded size of the `BodyPart`.
28
+ /// @return The number of bytes that the `BodyPart` struct would occupy when SCALE-encoded.
29
+ function encodedSizeAt(
30
+ bytes memory data,
31
+ uint256 offset
32
+ ) internal pure returns (uint256) {
33
+ if (offset >= data.length) revert InvalidBodyPartLength();
34
+
35
+ uint8 bodyPartType = uint8(data[offset]);
36
+ uint256 payloadLength;
37
+ if (bodyPartType == uint8(BodyPartType.Voice)) {
38
+ payloadLength = 0;
39
+ } else if (bodyPartType == uint8(BodyPartType.Members)) {
40
+ payloadLength = Compact.encodedSizeAt(data, offset + 1);
41
+ } else if (
42
+ bodyPartType == uint8(BodyPartType.Fraction) ||
43
+ bodyPartType == uint8(BodyPartType.AtLeastProportion) ||
44
+ bodyPartType == uint8(BodyPartType.MoreThanProportion)
45
+ ) {
46
+ uint256 innerLength = Compact.encodedSizeAt(data, offset + 1);
47
+ payloadLength =
48
+ innerLength +
49
+ Compact.encodedSizeAt(data, offset + 1 + innerLength);
50
+ } else {
51
+ revert InvalidBodyPartType(bodyPartType);
52
+ }
53
+
54
+ if (data.length < offset + 1 + payloadLength) {
55
+ revert InvalidBodyPartLength();
56
+ }
57
+
58
+ return 1 + payloadLength;
59
+ }
60
+
61
+ /// @notice Decodes a `BodyPart` struct from bytes starting at the beginning of the data.
62
+ /// @param data The byte sequence containing the encoded `BodyPart`.
63
+ /// @return bodyPart The decoded `BodyPart` struct.
64
+ /// @return bytesRead The total number of bytes read from the input data to decode the `BodyPart`.
65
+ function decode(
66
+ bytes memory data
67
+ ) internal pure returns (BodyPart memory bodyPart, uint256 bytesRead) {
68
+ return decodeAt(data, 0);
69
+ }
70
+
71
+ /// @notice Decodes a `BodyPart` struct from bytes starting at a given offset.
72
+ /// @param data The byte sequence containing the encoded `BodyPart`.
73
+ /// @param offset The starting index in `data` from which to decode the `BodyPart`.
74
+ /// @return bodyPart The decoded `BodyPart` struct.
75
+ /// @return bytesRead The total number of bytes read from the input data to decode the `BodyPart`.
76
+ function decodeAt(
77
+ bytes memory data,
78
+ uint256 offset
79
+ ) internal pure returns (BodyPart memory bodyPart, uint256 bytesRead) {
80
+ if (offset >= data.length) revert InvalidBodyPartLength();
81
+
82
+ uint8 bodyPartType = uint8(data[offset]);
83
+ uint256 payloadLength = encodedSizeAt(data, offset) - 1; // total size minus 1 byte for the bodyPartType
84
+
85
+ bytes memory payload = new bytes(payloadLength);
86
+ for (uint256 i = 0; i < payloadLength; i++) {
87
+ payload[i] = data[offset + 1 + i];
88
+ }
89
+
90
+ bodyPart = BodyPart({
91
+ bodyPartType: BodyPartType(bodyPartType),
92
+ payload: payload
93
+ });
94
+ bytesRead = 1 + payloadLength;
95
+ }
96
+
97
+ /// @notice Decodes a `Members` body part to extract the member count.
98
+ /// @param bodyPart The `BodyPart` struct to decode, which must be of type `Members`.
99
+ /// @return count The number of members encoded in the body part's payload.
100
+ function asMembers(
101
+ BodyPart memory bodyPart
102
+ ) internal pure returns (uint32 count) {
103
+ if (bodyPart.bodyPartType != BodyPartType.Members) {
104
+ revert InvalidBodyPartType(uint8(bodyPart.bodyPartType));
105
+ }
106
+ (uint256 decodedCount, ) = Compact.decode(bodyPart.payload);
107
+ count = uint32(decodedCount);
108
+ }
109
+
110
+ /// @notice Decodes a `Fraction`, `AtLeastProportion`, or `MoreThanProportion` body part to extract the nominator and denominator.
111
+ /// @param bodyPart The `BodyPart` struct to decode, which must be of type `Fraction`, `AtLeastProportion`, or `MoreThanProportion`.
112
+ /// @return nominator The numerator of the proportion encoded in the body part's payload.
113
+ /// @return denominator The denominator of the proportion encoded in the body part's payload.
114
+ function asProportion(
115
+ BodyPart memory bodyPart
116
+ ) internal pure returns (uint32 nominator, uint32 denominator) {
117
+ if (
118
+ bodyPart.bodyPartType != BodyPartType.Fraction &&
119
+ bodyPart.bodyPartType != BodyPartType.AtLeastProportion &&
120
+ bodyPart.bodyPartType != BodyPartType.MoreThanProportion
121
+ ) {
122
+ revert InvalidBodyPartType(uint8(bodyPart.bodyPartType));
123
+ }
124
+ (uint256 decodedNominator, uint256 offset) = Compact.decode(
125
+ bodyPart.payload
126
+ );
127
+ (uint256 decodedDenominator, ) = Compact.decodeAt(
128
+ bodyPart.payload,
129
+ offset
130
+ );
131
+ nominator = uint32(decodedNominator);
132
+ denominator = uint32(decodedDenominator);
133
+ }
134
+ }
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {HintType} from "./Hint/Hint.sol";
5
+
6
+ // Maximum number of items we expect in a single Assets value.
7
+ uint64 constant MAX_ITEMS_IN_ASSETS = 20;
8
+ // Maximum number of PalletsInfo entries we expect in a single `PalletsInfo` value.
9
+ uint32 constant MAX_PALLETS_INFO = 64;
10
+ // Maximum number of entries in `InitiateTransfer.assets` (BoundedVec<AssetTransferFilter, MaxAssetTransferFilters> in Rust).
11
+ uint32 constant MAX_ASSET_TRANSFER_FILTERS = 6;
12
+ // Maximum length of a pallet name in bytes.
13
+ uint32 constant MAX_PALLET_NAME_LEN = 48;
14
+ // Maximum number of hints in a `SetHints` instruction, equal to the number of variants in the `Hint` enum.
15
+ uint32 constant HINT_NUM_VARIANTS = uint32(type(HintType).max) + 1;
@@ -0,0 +1,60 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Compact} from "../../../Scale/Compact.sol";
5
+ import {AssetInstance} from "../AssetInstance/AssetInstance.sol";
6
+ import {AssetInstanceCodec} from "../AssetInstance/AssetInstanceCodec.sol";
7
+
8
+ /// @dev Discriminant for the different types of Fungibility in XCM v5.
9
+ enum FungibilityType {
10
+ /// @custom:variant A fungible asset; we record a number of units, as a `uint128` in the inner item.
11
+ Fungible,
12
+ /// @custom:variant A non-fungible asset. We record the instance identifier in the inner item. Only one asset of each instance identifier may ever be in existence at once.
13
+ NonFungible
14
+ }
15
+
16
+ /// @notice Classification of whether an asset is fungible or not, along with a mandatory amount or instance.
17
+ struct Fungibility {
18
+ /// @custom:property The type of fungibility, determining how to interpret the payload. See `FungibilityType` enum for possible values.
19
+ FungibilityType fType;
20
+ /// @custom:property The encoded payload containing the fungibility data, whose structure depends on the `fType`.
21
+ bytes payload;
22
+ }
23
+
24
+ /// @notice Parameters for the `Fungible` variant.
25
+ struct FungibleParams {
26
+ /// @custom:property The number of units of the fungible asset.
27
+ uint128 amount;
28
+ }
29
+
30
+ /// @notice Parameters for the `NonFungible` variant.
31
+ struct NonFungibleParams {
32
+ /// @custom:property The specific non-fungible asset instance.
33
+ AssetInstance instance;
34
+ }
35
+
36
+ // ============ Factory Functions ============
37
+
38
+ /// @notice Creates a `Fungibility` struct representing a fungible asset with the given amount.
39
+ /// @param params Parameters for the fungible variant.
40
+ function fungible(
41
+ FungibleParams memory params
42
+ ) pure returns (Fungibility memory) {
43
+ return
44
+ Fungibility({
45
+ fType: FungibilityType.Fungible,
46
+ payload: Compact.encode(params.amount)
47
+ });
48
+ }
49
+
50
+ /// @notice Creates a `Fungibility` struct representing a non-fungible asset with the given instance identifier.
51
+ /// @param params Parameters for the non-fungible variant.
52
+ function nonFungible(
53
+ NonFungibleParams memory params
54
+ ) pure returns (Fungibility memory) {
55
+ return
56
+ Fungibility({
57
+ fType: FungibilityType.NonFungible,
58
+ payload: AssetInstanceCodec.encode(params.instance)
59
+ });
60
+ }
@@ -0,0 +1,128 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Compact} from "../../../Scale/Compact.sol";
5
+ import {AssetInstance} from "../AssetInstance/AssetInstance.sol";
6
+ import {AssetInstanceCodec} from "../AssetInstance/AssetInstanceCodec.sol";
7
+ import {Fungibility, FungibilityType} from "./Fungibility.sol";
8
+
9
+ /// @title SCALE Codec for XCM v5 `Fungibility`
10
+ /// @notice SCALE-compliant encoder/decoder for the `Fungibility` type.
11
+ /// @dev SCALE reference: https://docs.polkadot.com/polkadot-protocol/basics/data-encoding
12
+ /// @dev XCM v5 reference: https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v5/index.html
13
+ library FungibilityCodec {
14
+ error InvalidFungibilityLength();
15
+ error InvalidFungibilityType(uint8 fType);
16
+ error InvalidFungibilityPayload();
17
+
18
+ /// @notice Encodes a `Fungibility` struct into bytes.
19
+ /// @param fungibility The `Fungibility` struct to encode.
20
+ /// @return SCALE-encoded byte sequence representing the `Fungibility`.
21
+ function encode(
22
+ Fungibility memory fungibility
23
+ ) internal pure returns (bytes memory) {
24
+ return abi.encodePacked(uint8(fungibility.fType), fungibility.payload);
25
+ }
26
+
27
+ /// @notice Returns the number of bytes that a `Fungibility` struct would occupy when SCALE-encoded, starting at a given offset in the data.
28
+ /// @param data The byte sequence containing the encoded `Fungibility`.
29
+ /// @param offset The starting index in `data` from which to calculate the encoded size of the `Fungibility`.
30
+ /// @return The number of bytes that the `Fungibility` struct would occupy when SCALE-encoded, starting at the given offset.
31
+ function encodedSizeAt(
32
+ bytes memory data,
33
+ uint256 offset
34
+ ) internal pure returns (uint256) {
35
+ if (data.length < offset + 1) {
36
+ revert InvalidFungibilityLength();
37
+ }
38
+ uint8 fType = uint8(data[offset]);
39
+ uint256 payloadLength;
40
+ ++offset;
41
+ if (fType == uint8(FungibilityType.Fungible)) {
42
+ payloadLength = Compact.encodedSizeAt(data, offset);
43
+ } else if (fType == uint8(FungibilityType.NonFungible)) {
44
+ payloadLength = AssetInstanceCodec.encodedSizeAt(data, offset);
45
+ } else {
46
+ revert InvalidFungibilityType(fType);
47
+ }
48
+
49
+ if (data.length < offset + payloadLength) {
50
+ revert InvalidFungibilityLength();
51
+ }
52
+
53
+ return 1 + payloadLength;
54
+ }
55
+
56
+ /// @notice Decodes a `Fungibility` instance from bytes starting at the beginning of the data.
57
+ /// @param data The byte sequence containing the encoded `Fungibility`.
58
+ /// @return fungibility The decoded `Fungibility` struct.
59
+ /// @return bytesRead The total number of bytes read from the input data to decode the `Fungibility`.
60
+ function decode(
61
+ bytes memory data
62
+ )
63
+ internal
64
+ pure
65
+ returns (Fungibility memory fungibility, uint256 bytesRead)
66
+ {
67
+ return decodeAt(data, 0);
68
+ }
69
+
70
+ /// @notice Decodes a `Fungibility` instance from bytes starting at a given offset.
71
+ /// @param data The byte sequence containing the encoded `Fungibility`.
72
+ /// @param offset The starting index in `data` from which to decode the `Fungibility`.
73
+ /// @return fungibility The decoded `Fungibility` struct.
74
+ /// @return bytesRead The total number of bytes read from the input data to decode the `Fungibility`.
75
+ function decodeAt(
76
+ bytes memory data,
77
+ uint256 offset
78
+ )
79
+ internal
80
+ pure
81
+ returns (Fungibility memory fungibility, uint256 bytesRead)
82
+ {
83
+ if (data.length < offset + 1) {
84
+ revert InvalidFungibilityLength();
85
+ }
86
+ uint8 fType = uint8(data[offset]);
87
+ uint256 payloadLength = encodedSizeAt(data, offset) - 1; // subtract 1 byte for the fType
88
+ bytes memory payload = new bytes(payloadLength);
89
+ for (uint256 i = 0; i < payloadLength; i++) {
90
+ payload[i] = data[offset + 1 + i];
91
+ }
92
+
93
+ return (
94
+ Fungibility({fType: FungibilityType(fType), payload: payload}),
95
+ 1 + payloadLength
96
+ );
97
+ }
98
+
99
+ /// @notice Decodes a `Fungibility` struct representing a fungible asset and extracts the amount.
100
+ /// @param fungibility The `Fungibility` struct to decode, which must have `fType` equal to `FungibilityType.Fungible`.
101
+ /// @return amount The number of units of the fungible asset, as a `uint128`.
102
+ function asFungible(
103
+ Fungibility memory fungibility
104
+ ) internal pure returns (uint128 amount) {
105
+ if (fungibility.fType != FungibilityType.Fungible) {
106
+ revert InvalidFungibilityType(uint8(fungibility.fType));
107
+ }
108
+ (uint256 decodedAmount, ) = Compact.decode(fungibility.payload);
109
+ if (decodedAmount > type(uint128).max) {
110
+ revert InvalidFungibilityPayload();
111
+ }
112
+ unchecked {
113
+ amount = uint128(decodedAmount);
114
+ }
115
+ }
116
+
117
+ /// @notice Decodes a `Fungibility` struct representing a non-fungible asset and extracts the instance identifier.
118
+ /// @param fungibility The `Fungibility` struct to decode, which must have `fType` equal to `FungibilityType.NonFungible`.
119
+ /// @return instance The `AssetInstance` struct identifying the specific instance of the non-fungible asset.
120
+ function asNonFungible(
121
+ Fungibility memory fungibility
122
+ ) internal pure returns (AssetInstance memory instance) {
123
+ if (fungibility.fType != FungibilityType.NonFungible) {
124
+ revert InvalidFungibilityType(uint8(fungibility.fType));
125
+ }
126
+ (instance, ) = AssetInstanceCodec.decode(fungibility.payload);
127
+ }
128
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Location} from "../Location/Location.sol";
5
+ import {LocationCodec} from "../Location/LocationCodec.sol";
6
+
7
+ /// @notice Discriminant for the `Hint` enum.
8
+ enum HintType {
9
+ /// @custom:variant Set asset claimer for all the trapped assets during the execution.
10
+ AssetClaimer
11
+ }
12
+
13
+ /// @notice A hint for XCM execution, changing the behaviour of the XCM program.
14
+ struct Hint {
15
+ /// @custom:property The type of the hint. See `HintType` enum for possible values.
16
+ HintType hType;
17
+ /// @custom:property The SCALE-encoded payload of the hint. Structure depends on `hType`.
18
+ bytes payload;
19
+ }
20
+
21
+ /// @notice Parameters for the `AssetClaimer` variant.
22
+ struct AssetClaimerParams {
23
+ /// @custom:property The claimer location.
24
+ Location location;
25
+ }
26
+
27
+ // ============ Factory Functions ============
28
+
29
+ /// @notice Creates an `AssetClaimer` hint.
30
+ /// @param params Parameters for the asset-claimer variant.
31
+ /// @return A `Hint` struct representing the `AssetClaimer` hint.
32
+ function assetClaimer(
33
+ AssetClaimerParams memory params
34
+ ) pure returns (Hint memory) {
35
+ return
36
+ Hint({
37
+ hType: HintType.AssetClaimer,
38
+ payload: LocationCodec.encode(params.location)
39
+ });
40
+ }