diff --git a/Cargo.lock b/Cargo.lock
index dad578ba0c1bcacb711dbcb5c2bfbaece2f141fa..cee1e2ce7411a5872abd4589f0fa4d099b39e84f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -31675,6 +31675,7 @@ name = "xcm-procedural"
 version = "7.0.0"
 dependencies = [
  "Inflector",
+ "frame-support 28.0.0",
  "proc-macro2 1.0.86",
  "quote 1.0.37",
  "staging-xcm 7.0.0",
diff --git a/bridges/snowbridge/pallets/inbound-queue/src/test.rs b/bridges/snowbridge/pallets/inbound-queue/src/test.rs
index 5386b845f2ec18cdb3e144089bc18c26edab3651..1e0bd8acc9257123ec49c28bc9cfe1b490e4debe 100644
--- a/bridges/snowbridge/pallets/inbound-queue/src/test.rs
+++ b/bridges/snowbridge/pallets/inbound-queue/src/test.rs
@@ -40,8 +40,8 @@ fn test_submit_happy_path() {
 				.into(),
 			nonce: 1,
 			message_id: [
-				97, 161, 116, 204, 182, 115, 192, 144, 130, 243, 240, 193, 122, 154, 108, 91, 247,
-				41, 226, 237, 202, 158, 238, 239, 210, 8, 147, 131, 84, 146, 171, 176,
+				86, 101, 80, 125, 84, 10, 227, 145, 230, 209, 152, 38, 206, 251, 206, 208, 244,
+				221, 22, 215, 1, 252, 79, 181, 99, 207, 166, 220, 98, 3, 81, 7,
 			],
 			fee_burned: 110000000000,
 		}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_asset_claimer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_asset_claimer.rs
index 544b05360521e50b2f99c4b47ab7282eb91187bd..bc00106b47c149468fbc53811b22fe7d4def5e2d 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_asset_claimer.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_asset_claimer.rs
@@ -44,7 +44,7 @@ fn test_set_asset_claimer_within_a_chain() {
 
 	type RuntimeCall = <AssetHubWestend as Chain>::RuntimeCall;
 	let asset_trap_xcm = Xcm::<RuntimeCall>::builder_unsafe()
-		.set_asset_claimer(bob_location.clone())
+		.set_hints(vec![AssetClaimer { location: bob_location.clone() }])
 		.withdraw_asset(assets.clone())
 		.clear_origin()
 		.build();
@@ -116,7 +116,7 @@ fn test_set_asset_claimer_between_the_chains() {
 	let assets: Assets = (Parent, trap_amount).into();
 	type RuntimeCall = <BridgeHubWestend as Chain>::RuntimeCall;
 	let trap_xcm = Xcm::<RuntimeCall>::builder_unsafe()
-		.set_asset_claimer(alice_bh_sibling.clone())
+		.set_hints(vec![AssetClaimer { location: alice_bh_sibling.clone() }])
 		.withdraw_asset(assets.clone())
 		.clear_origin()
 		.build();
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs
index 74f56403740098c2d2fc64538645fe8ae86c14f8..ccf473484cad0c0398ac07cb85af3274b87ab43d 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs
@@ -22,6 +22,7 @@ use alloc::vec::Vec;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -176,8 +177,16 @@ impl<Call> XcmWeightInfo<Call> for AssetHubRococoXcmWeight<Call> {
 	fn clear_error() -> Weight {
 		XcmGeneric::<Runtime>::clear_error()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight {
 		XcmGeneric::<Runtime>::claim_asset()
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index b69c136b29d98779f4150915c5e8b03b8d0d33e0..d48debef94c88eaf10dd76c92f501e1e856f3868 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -87,7 +87,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 5_803_000 picoseconds.
 		Weight::from_parts(5_983_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs
index ff99f1242b22931d319bbe78b06bffd053a99a61..a0e9705ff01dc99b8323081a49ac8f0009e8d472 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs
@@ -21,6 +21,7 @@ use alloc::vec::Vec;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -176,8 +177,16 @@ impl<Call> XcmWeightInfo<Call> for AssetHubWestendXcmWeight<Call> {
 	fn clear_error() -> Weight {
 		XcmGeneric::<Runtime>::clear_error()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight {
 		XcmGeneric::<Runtime>::claim_asset()
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index 528694123115f440dae61a64f9c3560580e3cb7c..0ec2741c0490a5723a3e794cf6f35820e5b98f62 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -87,7 +87,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 5_580_000 picoseconds.
 		Weight::from_parts(5_950_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs
index e5c6f493d6dcf503561e74ea71b0a364e42d226b..efc2798999bf1ad791994acef580a849ad53c447 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs
@@ -22,6 +22,7 @@ use codec::Encode;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -257,8 +258,16 @@ impl<Call> XcmWeightInfo<Call> for BridgeHubRococoXcmWeight<Call> {
 	fn unpaid_execution(_: &WeightLimit, _: &Option<Location>) -> Weight {
 		XcmGeneric::<Runtime>::unpaid_execution()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn execute_with_origin(_: &Option<InteriorLocation>, _: &Xcm<Call>) -> Weight {
 		XcmGeneric::<Runtime>::execute_with_origin()
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index bac73e0e056739c304f714e733a2f1ba9f6de219..daf22190a42b639c67a596f553c0b99c4cbed632 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -373,7 +373,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 1_085_000 picoseconds.
 		Weight::from_parts(1_161_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs
index 939b1c7a287b782afef5293631dd2d6ced01ae30..15a1dae09d9bb6afb3ff4c5babfe2624785826ac 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs
@@ -23,6 +23,7 @@ use codec::Encode;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -178,8 +179,16 @@ impl<Call> XcmWeightInfo<Call> for BridgeHubWestendXcmWeight<Call> {
 	fn clear_error() -> Weight {
 		XcmGeneric::<Runtime>::clear_error()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight {
 		XcmGeneric::<Runtime>::claim_asset()
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index 6434f6206fbe14d2c7dfa2b004e2b21f79e791ac..03cbaa866ad8041d4131477f59fa8dd0a908486d 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -373,7 +373,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 995_000 picoseconds.
 		Weight::from_parts(1_060_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs
index 2c4a97601c644358d96f3ca3928658ac801eec58..dc21e2ea117fbfd9877e5a1f1c31e41d6d1b8400 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs
@@ -22,6 +22,7 @@ use alloc::vec::Vec;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -255,8 +256,16 @@ impl<Call> XcmWeightInfo<Call> for CoretimeRococoXcmWeight<Call> {
 	fn unpaid_execution(_: &WeightLimit, _: &Option<Location>) -> Weight {
 		XcmGeneric::<Runtime>::unpaid_execution()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn execute_with_origin(_: &Option<InteriorLocation>, _: &Xcm<Call>) -> Weight {
 		XcmGeneric::<Runtime>::execute_with_origin()
diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index d207c09ffcd8d10205f4879af1c700800c354fdd..cdcba6134bf82876d18369bf903ec6c2671edfe8 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -331,7 +331,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 650_000 picoseconds.
 		Weight::from_parts(673_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs
index 906088a1df86f8092c30e3d69174780852de6cef..29466b3718c17529dbfe89d40eef50b2c8a03704 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs
@@ -21,6 +21,7 @@ use alloc::vec::Vec;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -176,8 +177,16 @@ impl<Call> XcmWeightInfo<Call> for CoretimeWestendXcmWeight<Call> {
 	fn clear_error() -> Weight {
 		XcmGeneric::<Runtime>::clear_error()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight {
 		XcmGeneric::<Runtime>::claim_asset()
diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index fb6e4631736d6689b8a863b4759620098f25242f..6c6d3cf8c525ca57d1107a30d1b83ece5bd62894 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -331,7 +331,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 624_000 picoseconds.
 		Weight::from_parts(659_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs
index 47008a2943e56dd65fa488a35b90062ee0b81eaf..d55198f60a00ceddc61ae8ee26dac48e67856e4d 100644
--- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs
@@ -21,6 +21,7 @@ use alloc::vec::Vec;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -254,8 +255,16 @@ impl<Call> XcmWeightInfo<Call> for PeopleRococoXcmWeight<Call> {
 	fn unpaid_execution(_: &WeightLimit, _: &Option<Location>) -> Weight {
 		XcmGeneric::<Runtime>::unpaid_execution()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn execute_with_origin(_: &Option<InteriorLocation>, _: &Xcm<Call>) -> Weight {
 		XcmGeneric::<Runtime>::execute_with_origin()
diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index 6aac6119e7ec5bea8fe064fdd90c61795fb0d6df..caa91650734821681f5d2db4157422794f8e15a6 100644
--- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -331,7 +331,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 685_000 picoseconds.
 		Weight::from_parts(757_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs
index 27fd499ebba72ff7688fd39c265f0a8e306c0f2b..915a499cb77c8d47a016a483c8c4df50411c8030 100644
--- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs
+++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs
@@ -21,6 +21,7 @@ use alloc::vec::Vec;
 use frame_support::weights::Weight;
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::{
 	latest::{prelude::*, AssetTransferFilter},
 	DoubleEncoded,
@@ -254,8 +255,16 @@ impl<Call> XcmWeightInfo<Call> for PeopleWestendXcmWeight<Call> {
 	fn unpaid_execution(_: &WeightLimit, _: &Option<Location>) -> Weight {
 		XcmGeneric::<Runtime>::unpaid_execution()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn execute_with_origin(_: &Option<InteriorLocation>, _: &Xcm<Call>) -> Weight {
 		XcmGeneric::<Runtime>::execute_with_origin()
diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index 36400f2c1e6687130cb99ebeb3b5276bee2ac0bf..ad2cde22a0755735c001aaa26f2242b830167cdc 100644
--- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -331,7 +331,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 598_000 picoseconds.
 		Weight::from_parts(655_000, 0)
 	}
-	pub fn set_asset_claimer() -> Weight {
+	pub fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/polkadot/runtime/rococo/src/weights/xcm/mod.rs b/polkadot/runtime/rococo/src/weights/xcm/mod.rs
index 16f51a778917c9f0061ea84cb9a4a55ed1953417..eb27e5c5a8979c9e8996d03e8ecb466d104182be 100644
--- a/polkadot/runtime/rococo/src/weights/xcm/mod.rs
+++ b/polkadot/runtime/rococo/src/weights/xcm/mod.rs
@@ -24,6 +24,7 @@ use xcm::{latest::prelude::*, DoubleEncoded};
 
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmBalancesWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::latest::AssetTransferFilter;
 
 /// Types of asset supported by the Rococo runtime.
@@ -290,8 +291,16 @@ impl<RuntimeCall> XcmWeightInfo<RuntimeCall> for RococoXcmWeight<RuntimeCall> {
 	fn unpaid_execution(_: &WeightLimit, _: &Option<Location>) -> Weight {
 		XcmGeneric::<Runtime>::unpaid_execution()
 	}
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
 	fn execute_with_origin(_: &Option<InteriorLocation>, _: &Xcm<RuntimeCall>) -> Weight {
 		XcmGeneric::<Runtime>::execute_with_origin()
diff --git a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index e5915a7986bf995ee8aeb72c0d8b71e33007a509..2dc8880c83265fd52cdb63ca01f50d292048dcb4 100644
--- a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -82,7 +82,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 2_899_000 picoseconds.
 		Weight::from_parts(3_090_000, 0)
 	}
-	pub(crate) fn set_asset_claimer() -> Weight {
+	pub(crate) fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/polkadot/runtime/westend/src/weights/xcm/mod.rs b/polkadot/runtime/westend/src/weights/xcm/mod.rs
index 60265445334d3e78d39e50bd30ef417c560653d1..d2691c998d990d0ae3e9f77f12f9f6201c0a396f 100644
--- a/polkadot/runtime/westend/src/weights/xcm/mod.rs
+++ b/polkadot/runtime/westend/src/weights/xcm/mod.rs
@@ -27,6 +27,7 @@ use xcm::{
 
 use pallet_xcm_benchmarks_fungible::WeightInfo as XcmBalancesWeight;
 use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
+use sp_runtime::BoundedVec;
 use xcm::latest::AssetTransferFilter;
 
 /// Types of asset supported by the westend runtime.
@@ -208,11 +209,17 @@ impl<RuntimeCall> XcmWeightInfo<RuntimeCall> for WestendXcmWeight<RuntimeCall> {
 	fn clear_error() -> Weight {
 		XcmGeneric::<Runtime>::clear_error()
 	}
-
-	fn set_asset_claimer(_location: &Location) -> Weight {
-		XcmGeneric::<Runtime>::set_asset_claimer()
+	fn set_hints(hints: &BoundedVec<Hint, HintNumVariants>) -> Weight {
+		let mut weight = Weight::zero();
+		for hint in hints {
+			match hint {
+				AssetClaimer { .. } => {
+					weight = weight.saturating_add(XcmGeneric::<Runtime>::asset_claimer());
+				},
+			}
+		}
+		weight
 	}
-
 	fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight {
 		XcmGeneric::<Runtime>::claim_asset()
 	}
diff --git a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
index 076744a59753ab3d98fe2e7762708b6c43136769..dfc02fd20bc3b1d27303006b5936efb200d44fb4 100644
--- a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
+++ b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
@@ -82,7 +82,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
 		// Minimum execution time: 3_096_000 picoseconds.
 		Weight::from_parts(3_313_000, 0)
 	}
-	pub(crate) fn set_asset_claimer() -> Weight {
+	pub(crate) fn asset_claimer() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs
index 285322891c63aad74550f4cc07f26b51bbfa386e..431c7a5f1371771318c795928b54cee66ed9ff40 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs
@@ -20,7 +20,7 @@ use crate::{account_and_location, new_executor, EnsureDelivery, XcmCallOf};
 use alloc::{vec, vec::Vec};
 use codec::Encode;
 use frame_benchmarking::v2::*;
-use frame_support::traits::fungible::Inspect;
+use frame_support::{traits::fungible::Inspect, BoundedVec};
 use xcm::{
 	latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight, MAX_ITEMS_IN_ASSETS},
 	DoubleEncoded,
@@ -144,7 +144,11 @@ mod benchmarks {
 		let mut executor = new_executor::<T>(Default::default());
 		let (_, sender_location) = account_and_location::<T>(1);
 
-		let instruction = Instruction::SetAssetClaimer { location: sender_location.clone() };
+		let instruction = Instruction::SetHints {
+			hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
+				location: sender_location.clone(),
+			}]),
+		};
 
 		let xcm = Xcm(vec![instruction]);
 		#[block]
diff --git a/polkadot/xcm/procedural/Cargo.toml b/polkadot/xcm/procedural/Cargo.toml
index 3167766158ff4e95379827c921d9ea9bac251ae4..88ed3c94ddf4fe1ef99cfc60f49e79feb6c23a37 100644
--- a/polkadot/xcm/procedural/Cargo.toml
+++ b/polkadot/xcm/procedural/Cargo.toml
@@ -26,3 +26,5 @@ trybuild = { features = ["diff"], workspace = true }
 # NOTE: we have to explicitly specify `std` because of trybuild
 # https://github.com/paritytech/polkadot-sdk/pull/5167
 xcm = { workspace = true, default-features = true, features = ["std"] }
+# For testing macros.
+frame-support = { workspace = true }
diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs
index b65290332af9808809fbca4b42deb77f63d90696..34b89f13422c0ef16b4ac514722861630f644f23 100644
--- a/polkadot/xcm/procedural/src/builder_pattern.rs
+++ b/polkadot/xcm/procedural/src/builder_pattern.rs
@@ -20,8 +20,8 @@ use inflector::Inflector;
 use proc_macro2::TokenStream as TokenStream2;
 use quote::{format_ident, quote};
 use syn::{
-	Data, DataEnum, DeriveInput, Error, Expr, ExprLit, Fields, Ident, Lit, Meta, MetaNameValue,
-	Result, Variant,
+	Data, DataEnum, DeriveInput, Error, Expr, ExprLit, Fields, GenericArgument, Ident, Lit, Meta,
+	MetaNameValue, PathArguments, Result, Type, TypePath, Variant,
 };
 
 pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
@@ -29,7 +29,7 @@ pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
 		Data::Enum(data_enum) => data_enum,
 		_ => return Err(Error::new_spanned(&input, "Expected the `Instruction` enum")),
 	};
-	let builder_raw_impl = generate_builder_raw_impl(&input.ident, data_enum);
+	let builder_raw_impl = generate_builder_raw_impl(&input.ident, data_enum)?;
 	let builder_impl = generate_builder_impl(&input.ident, data_enum)?;
 	let builder_unpaid_impl = generate_builder_unpaid_impl(&input.ident, data_enum)?;
 	let output = quote! {
@@ -83,54 +83,12 @@ pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
 	Ok(output)
 }
 
-fn generate_builder_raw_impl(name: &Ident, data_enum: &DataEnum) -> TokenStream2 {
-	let methods = data_enum.variants.iter().map(|variant| {
-		let variant_name = &variant.ident;
-		let method_name_string = &variant_name.to_string().to_snake_case();
-		let method_name = syn::Ident::new(method_name_string, variant_name.span());
-		let docs = get_doc_comments(variant);
-		let method = match &variant.fields {
-			Fields::Unit => {
-				quote! {
-					pub fn #method_name(mut self) -> Self {
-						self.instructions.push(#name::<Call>::#variant_name);
-						self
-					}
-				}
-			},
-			Fields::Unnamed(fields) => {
-				let arg_names: Vec<_> = fields
-					.unnamed
-					.iter()
-					.enumerate()
-					.map(|(index, _)| format_ident!("arg{}", index))
-					.collect();
-				let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty).collect();
-				quote! {
-					pub fn #method_name(mut self, #(#arg_names: impl Into<#arg_types>),*) -> Self {
-						#(let #arg_names = #arg_names.into();)*
-						self.instructions.push(#name::<Call>::#variant_name(#(#arg_names),*));
-						self
-					}
-				}
-			},
-			Fields::Named(fields) => {
-				let arg_names: Vec<_> = fields.named.iter().map(|field| &field.ident).collect();
-				let arg_types: Vec<_> = fields.named.iter().map(|field| &field.ty).collect();
-				quote! {
-					pub fn #method_name(mut self, #(#arg_names: impl Into<#arg_types>),*) -> Self {
-						#(let #arg_names = #arg_names.into();)*
-						self.instructions.push(#name::<Call>::#variant_name { #(#arg_names),* });
-						self
-					}
-				}
-			},
-		};
-		quote! {
-			#(#docs)*
-			#method
-		}
-	});
+fn generate_builder_raw_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStream2> {
+	let methods = data_enum
+		.variants
+		.iter()
+		.map(|variant| convert_variant_to_method(name, variant, None))
+		.collect::<Result<Vec<_>>>()?;
 	let output = quote! {
 		impl<Call> XcmBuilder<Call, AnythingGoes> {
 			#(#methods)*
@@ -140,7 +98,7 @@ fn generate_builder_raw_impl(name: &Ident, data_enum: &DataEnum) -> TokenStream2
 			}
 		}
 	};
-	output
+	Ok(output)
 }
 
 fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStream2> {
@@ -165,11 +123,17 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStre
 					"Expected `builder(loads_holding)` or `builder(pays_fees)`",
 				)
 			})?;
-			let ident_to_match: Ident = syn::parse_quote!(loads_holding);
-			if inner_ident == ident_to_match {
+			let loads_holding_ident: Ident = syn::parse_quote!(loads_holding);
+			let pays_fees_ident: Ident = syn::parse_quote!(pays_fees);
+			if inner_ident == loads_holding_ident {
 				Ok(Some(variant))
+			} else if inner_ident == pays_fees_ident {
+				Ok(None)
 			} else {
-				Ok(None) // Must have been `pays_fees` instead.
+				Err(Error::new_spanned(
+					&builder_attr,
+					"Expected `builder(loads_holding)` or `builder(pays_fees)`",
+				))
 			}
 		})
 		.collect::<Result<Vec<_>>>()?;
@@ -178,57 +142,14 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStre
 		.into_iter()
 		.flatten()
 		.map(|variant| {
-			let variant_name = &variant.ident;
-			let method_name_string = &variant_name.to_string().to_snake_case();
-			let method_name = syn::Ident::new(method_name_string, variant_name.span());
-			let docs = get_doc_comments(variant);
-			let method = match &variant.fields {
-				Fields::Unnamed(fields) => {
-					let arg_names: Vec<_> = fields
-						.unnamed
-						.iter()
-						.enumerate()
-						.map(|(index, _)| format_ident!("arg{}", index))
-						.collect();
-					let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty).collect();
-					quote! {
-						#(#docs)*
-						pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder<Call, LoadedHolding> {
-							let mut new_instructions = self.instructions;
-							#(let #arg_names = #arg_names.into();)*
-							new_instructions.push(#name::<Call>::#variant_name(#(#arg_names),*));
-							XcmBuilder {
-								instructions: new_instructions,
-								state: core::marker::PhantomData,
-							}
-						}
-					}
-				},
-				Fields::Named(fields) => {
-					let arg_names: Vec<_> = fields.named.iter().map(|field| &field.ident).collect();
-					let arg_types: Vec<_> = fields.named.iter().map(|field| &field.ty).collect();
-					quote! {
-						#(#docs)*
-						pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder<Call, LoadedHolding> {
-							let mut new_instructions = self.instructions;
-							#(let #arg_names = #arg_names.into();)*
-							new_instructions.push(#name::<Call>::#variant_name { #(#arg_names),* });
-							XcmBuilder {
-								instructions: new_instructions,
-								state: core::marker::PhantomData,
-							}
-						}
-					}
-				},
-				_ =>
-					return Err(Error::new_spanned(
-						variant,
-						"Instructions that load the holding register should take operands",
-					)),
-			};
+			let method = convert_variant_to_method(
+				name,
+				variant,
+				Some(quote! { XcmBuilder<Call, LoadedHolding> }),
+			)?;
 			Ok(method)
 		})
-		.collect::<std::result::Result<Vec<_>, _>>()?;
+		.collect::<Result<Vec<_>>>()?;
 
 	let first_impl = quote! {
 		impl<Call> XcmBuilder<Call, PaymentRequired> {
@@ -240,27 +161,12 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStre
 	let allowed_after_load_holding_methods: Vec<TokenStream2> = data_enum
 		.variants
 		.iter()
-		.filter(|variant| variant.ident == "ClearOrigin")
+		.filter(|variant| variant.ident == "ClearOrigin" || variant.ident == "SetHints")
 		.map(|variant| {
-			let variant_name = &variant.ident;
-			let method_name_string = &variant_name.to_string().to_snake_case();
-			let method_name = syn::Ident::new(method_name_string, variant_name.span());
-			let docs = get_doc_comments(variant);
-			let method = match &variant.fields {
-				Fields::Unit => {
-					quote! {
-						#(#docs)*
-						pub fn #method_name(mut self) -> XcmBuilder<Call, LoadedHolding> {
-							self.instructions.push(#name::<Call>::#variant_name);
-							self
-						}
-					}
-				},
-				_ => return Err(Error::new_spanned(variant, "ClearOrigin should have no fields")),
-			};
+			let method = convert_variant_to_method(name, variant, None)?;
 			Ok(method)
 		})
-		.collect::<std::result::Result<Vec<_>, _>>()?;
+		.collect::<Result<Vec<_>>>()?;
 
 	// Then we require fees to be paid
 	let pay_fees_variants = data_enum
@@ -295,36 +201,12 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStre
 		.into_iter()
 		.flatten()
 		.map(|variant| {
-			let variant_name = &variant.ident;
-			let method_name_string = &variant_name.to_string().to_snake_case();
-			let method_name = syn::Ident::new(method_name_string, variant_name.span());
-			let docs = get_doc_comments(variant);
-			let fields = match &variant.fields {
-				Fields::Named(fields) => {
-					let arg_names: Vec<_> =
-						fields.named.iter().map(|field| &field.ident).collect();
-					let arg_types: Vec<_> =
-						fields.named.iter().map(|field| &field.ty).collect();
-					quote! {
-						#(#docs)*
-						pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder<Call, AnythingGoes> {
-							let mut new_instructions = self.instructions;
-							#(let #arg_names = #arg_names.into();)*
-							new_instructions.push(#name::<Call>::#variant_name { #(#arg_names),* });
-							XcmBuilder {
-								instructions: new_instructions,
-								state: core::marker::PhantomData,
-							}
-						}
-					}
-				},
-				_ =>
-					return Err(Error::new_spanned(
-						variant,
-						"Both BuyExecution and PayFees have named fields",
-					)),
-			};
-			Ok(fields)
+			let method = convert_variant_to_method(
+				name,
+				variant,
+				Some(quote! { XcmBuilder<Call, AnythingGoes> }),
+			)?;
+			Ok(method)
 		})
 		.collect::<Result<Vec<_>>>()?;
 
@@ -349,35 +231,156 @@ fn generate_builder_unpaid_impl(name: &Ident, data_enum: &DataEnum) -> Result<To
 		.iter()
 		.find(|variant| variant.ident == "UnpaidExecution")
 		.ok_or(Error::new_spanned(&data_enum.variants, "No UnpaidExecution instruction"))?;
-	let unpaid_execution_ident = &unpaid_execution_variant.ident;
-	let unpaid_execution_method_name = Ident::new(
-		&unpaid_execution_ident.to_string().to_snake_case(),
-		unpaid_execution_ident.span(),
-	);
-	let docs = get_doc_comments(unpaid_execution_variant);
-	let fields = match &unpaid_execution_variant.fields {
-		Fields::Named(fields) => fields,
-		_ =>
-			return Err(Error::new_spanned(
-				unpaid_execution_variant,
-				"UnpaidExecution should have named fields",
-			)),
-	};
-	let arg_names: Vec<_> = fields.named.iter().map(|field| &field.ident).collect();
-	let arg_types: Vec<_> = fields.named.iter().map(|field| &field.ty).collect();
+	let method = convert_variant_to_method(
+		name,
+		&unpaid_execution_variant,
+		Some(quote! { XcmBuilder<Call, AnythingGoes> }),
+	)?;
 	Ok(quote! {
 		impl<Call> XcmBuilder<Call, ExplicitUnpaidRequired> {
-			#(#docs)*
-			pub fn #unpaid_execution_method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder<Call, AnythingGoes> {
-				let mut new_instructions = self.instructions;
-				#(let #arg_names = #arg_names.into();)*
-				new_instructions.push(#name::<Call>::#unpaid_execution_ident { #(#arg_names),* });
-				XcmBuilder {
-					instructions: new_instructions,
-					state: core::marker::PhantomData,
+			#method
+		}
+	})
+}
+
+// Have to call with `XcmBuilder<Call, LoadedHolding>` in allowed_after_load_holding_methods.
+fn convert_variant_to_method(
+	name: &Ident,
+	variant: &Variant,
+	maybe_return_type: Option<TokenStream2>,
+) -> Result<TokenStream2> {
+	let variant_name = &variant.ident;
+	let method_name_string = &variant_name.to_string().to_snake_case();
+	let method_name = syn::Ident::new(method_name_string, variant_name.span());
+	let docs = get_doc_comments(variant);
+	let method = match &variant.fields {
+		Fields::Unit =>
+			if let Some(return_type) = maybe_return_type {
+				quote! {
+					pub fn #method_name(self) -> #return_type {
+						let mut new_instructions = self.instructions;
+						new_instructions.push(#name::<Call>::#variant_name);
+						XcmBuilder {
+							instructions: new_instructions,
+							state: core::marker::PhantomData,
+						}
+					}
+				}
+			} else {
+				quote! {
+					pub fn #method_name(mut self) -> Self {
+						self.instructions.push(#name::<Call>::#variant_name);
+						self
+					}
+				}
+			},
+		Fields::Unnamed(fields) => {
+			let arg_names: Vec<_> = fields
+				.unnamed
+				.iter()
+				.enumerate()
+				.map(|(index, _)| format_ident!("arg{}", index))
+				.collect();
+			let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty).collect();
+			if let Some(return_type) = maybe_return_type {
+				quote! {
+					pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> #return_type {
+						let mut new_instructions = self.instructions;
+						#(let #arg_names = #arg_names.into();)*
+						new_instructions.push(#name::<Call>::#variant_name(#(#arg_names),*));
+						XcmBuilder {
+							instructions: new_instructions,
+							state: core::marker::PhantomData,
+						}
+					}
+				}
+			} else {
+				quote! {
+					pub fn #method_name(mut self, #(#arg_names: impl Into<#arg_types>),*) -> Self {
+						#(let #arg_names = #arg_names.into();)*
+						self.instructions.push(#name::<Call>::#variant_name(#(#arg_names),*));
+						self
+					}
 				}
 			}
-		}
+		},
+		Fields::Named(fields) => {
+			let normal_fields: Vec<_> = fields
+				.named
+				.iter()
+				.filter(|field| {
+					if let Type::Path(TypePath { path, .. }) = &field.ty {
+						for segment in &path.segments {
+							if segment.ident == format_ident!("BoundedVec") {
+								return false;
+							}
+						}
+						true
+					} else {
+						true
+					}
+				})
+				.collect();
+			let bounded_fields: Vec<_> = fields
+				.named
+				.iter()
+				.filter(|field| {
+					if let Type::Path(TypePath { path, .. }) = &field.ty {
+						for segment in &path.segments {
+							if segment.ident == format_ident!("BoundedVec") {
+								return true;
+							}
+						}
+						false
+					} else {
+						false
+					}
+				})
+				.collect();
+			let arg_names: Vec<_> = normal_fields.iter().map(|field| &field.ident).collect();
+			let arg_types: Vec<_> = normal_fields.iter().map(|field| &field.ty).collect();
+			let bounded_names: Vec<_> = bounded_fields.iter().map(|field| &field.ident).collect();
+			let bounded_types = bounded_fields
+				.iter()
+				.map(|field| extract_generic_argument(&field.ty, 0, "BoundedVec's inner type"))
+				.collect::<Result<Vec<_>>>()?;
+			let bounded_sizes = bounded_fields
+				.iter()
+				.map(|field| extract_generic_argument(&field.ty, 1, "BoundedVec's size"))
+				.collect::<Result<Vec<_>>>()?;
+			let comma_in_the_middle = if normal_fields.is_empty() {
+				quote! {}
+			} else {
+				quote! {,}
+			};
+			if let Some(return_type) = maybe_return_type {
+				quote! {
+					pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),* #comma_in_the_middle #(#bounded_names: Vec<#bounded_types>),*) -> #return_type {
+						let mut new_instructions = self.instructions;
+						#(let #arg_names = #arg_names.into();)*
+						#(let #bounded_names = BoundedVec::<#bounded_types, #bounded_sizes>::truncate_from(#bounded_names);)*
+						new_instructions.push(#name::<Call>::#variant_name { #(#arg_names),* #comma_in_the_middle #(#bounded_names),* });
+						XcmBuilder {
+							instructions: new_instructions,
+							state: core::marker::PhantomData,
+						}
+					}
+				}
+			} else {
+				quote! {
+					pub fn #method_name(mut self, #(#arg_names: impl Into<#arg_types>),* #comma_in_the_middle #(#bounded_names: Vec<#bounded_types>),*) -> Self {
+						#(let #arg_names = #arg_names.into();)*
+						#(let #bounded_names = BoundedVec::<#bounded_types, #bounded_sizes>::truncate_from(#bounded_names);)*
+						self.instructions.push(#name::<Call>::#variant_name { #(#arg_names),* #comma_in_the_middle #(#bounded_names),* });
+						self
+					}
+				}
+			}
+		},
+	};
+	Ok(quote! {
+		#(#docs)*
+		#method
 	})
 }
 
@@ -395,3 +398,40 @@ fn get_doc_comments(variant: &Variant) -> Vec<TokenStream2> {
 		.map(|doc| syn::parse_str::<TokenStream2>(&format!("/// {}", doc)).unwrap())
 		.collect()
 }
+
+fn extract_generic_argument<'a>(
+	field_ty: &'a Type,
+	index: usize,
+	expected_msg: &str,
+) -> Result<&'a Ident> {
+	if let Type::Path(type_path) = field_ty {
+		if let Some(segment) = type_path.path.segments.last() {
+			if let PathArguments::AngleBracketed(angle_brackets) = &segment.arguments {
+				let args: Vec<_> = angle_brackets.args.iter().collect();
+				if let Some(GenericArgument::Type(Type::Path(TypePath { path, .. }))) =
+					args.get(index)
+				{
+					return path.get_ident().ok_or_else(|| {
+						Error::new_spanned(
+							path,
+							format!("Expected an identifier for {}", expected_msg),
+						)
+					});
+				}
+				return Err(Error::new_spanned(
+					angle_brackets,
+					format!("Expected a generic argument at index {} for {}", index, expected_msg),
+				));
+			}
+			return Err(Error::new_spanned(
+				&segment.arguments,
+				format!("Expected angle-bracketed arguments for {}", expected_msg),
+			));
+		}
+		return Err(Error::new_spanned(
+			&type_path.path,
+			format!("Expected at least one path segment for {}", expected_msg),
+		));
+	}
+	Err(Error::new_spanned(field_ty, format!("Expected a path type for {}", expected_msg)))
+}
diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/loads_holding_no_operands.rs b/polkadot/xcm/procedural/src/enum_variants.rs
similarity index 51%
rename from polkadot/xcm/procedural/tests/ui/builder_pattern/loads_holding_no_operands.rs
rename to polkadot/xcm/procedural/src/enum_variants.rs
index 070f0be6bacc995aa38a341fa9d242d395c4e045..f9f2d9e156755f6a067ef96772d6f2dc51e42369 100644
--- a/polkadot/xcm/procedural/tests/ui/builder_pattern/loads_holding_no_operands.rs
+++ b/polkadot/xcm/procedural/src/enum_variants.rs
@@ -14,19 +14,25 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-//! Test error when an instruction that loads the holding register doesn't take operands.
-
-use xcm_procedural::Builder;
-
-struct Xcm<Call>(pub Vec<Instruction<Call>>);
-
-#[derive(Builder)]
-enum Instruction<Call> {
-    #[builder(loads_holding)]
-    WithdrawAsset,
-    BuyExecution { fees: u128 },
-    UnpaidExecution { weight_limit: (u32, u32) },
-    Transact { call: Call },
+//! Simple derive macro for getting the number of variants in an enum.
+
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{Data, DeriveInput, Error, Result};
+
+pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
+	let data_enum = match &input.data {
+		Data::Enum(data_enum) => data_enum,
+		_ => return Err(Error::new_spanned(&input, "Expected an enum.")),
+	};
+	let ident = format_ident!("{}NumVariants", input.ident);
+	let number_of_variants: usize = data_enum.variants.iter().count();
+	Ok(quote! {
+		pub struct #ident;
+		impl ::frame_support::traits::Get<u32> for #ident {
+			fn get() -> u32 {
+				#number_of_variants as u32
+			}
+		}
+	})
 }
-
-fn main() {}
diff --git a/polkadot/xcm/procedural/src/lib.rs b/polkadot/xcm/procedural/src/lib.rs
index 9971fdceb69a678ccd80f8191273ba6bfdef66f7..0dd270286f69ca53f23361cd229e8c8d7dda46fa 100644
--- a/polkadot/xcm/procedural/src/lib.rs
+++ b/polkadot/xcm/procedural/src/lib.rs
@@ -20,6 +20,7 @@ use proc_macro::TokenStream;
 use syn::{parse_macro_input, DeriveInput};
 
 mod builder_pattern;
+mod enum_variants;
 mod v3;
 mod v4;
 mod v5;
@@ -86,3 +87,11 @@ pub fn derive_builder(input: TokenStream) -> TokenStream {
 		.unwrap_or_else(syn::Error::into_compile_error)
 		.into()
 }
+
+#[proc_macro_derive(NumVariants)]
+pub fn derive_num_variants(input: TokenStream) -> TokenStream {
+	let input = parse_macro_input!(input as DeriveInput);
+	enum_variants::derive(input)
+		.unwrap_or_else(syn::Error::into_compile_error)
+		.into()
+}
diff --git a/polkadot/xcm/procedural/tests/builder_pattern.rs b/polkadot/xcm/procedural/tests/builder_pattern.rs
index 4202309bf3f71cb6a6a81968ad19eea2cc999515..3915621916d4b849bad270133e903253e4dfd121 100644
--- a/polkadot/xcm/procedural/tests/builder_pattern.rs
+++ b/polkadot/xcm/procedural/tests/builder_pattern.rs
@@ -17,6 +17,7 @@
 //! Test the methods generated by the Builder derive macro.
 //! Tests directly on the actual Xcm struct and Instruction enum.
 
+use frame_support::BoundedVec;
 use xcm::latest::prelude::*;
 
 #[test]
@@ -100,3 +101,61 @@ fn default_builder_allows_clear_origin_before_buy_execution() {
 		])
 	);
 }
+
+#[test]
+fn bounded_vecs_use_vecs_and_truncate_them() {
+	let claimer = Location::parent();
+	// We can use a vec instead of a bounded vec for specifying hints.
+	let xcm: Xcm<()> = Xcm::builder_unsafe()
+		.set_hints(vec![AssetClaimer { location: claimer.clone() }])
+		.build();
+	assert_eq!(
+		xcm,
+		Xcm(vec![SetHints {
+			hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
+				location: Location::parent()
+			},]),
+		},])
+	);
+
+	// If we include more than the limit they'll get truncated.
+	let xcm: Xcm<()> = Xcm::builder_unsafe()
+		.set_hints(vec![
+			AssetClaimer { location: claimer.clone() },
+			AssetClaimer { location: Location::here() },
+		])
+		.build();
+	assert_eq!(
+		xcm,
+		Xcm(vec![SetHints {
+			hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
+				location: Location::parent()
+			},]),
+		},])
+	);
+
+	let xcm: Xcm<()> = Xcm::builder()
+		.withdraw_asset((Here, 100u128))
+		.set_hints(vec![AssetClaimer { location: claimer }])
+		.clear_origin()
+		.pay_fees((Here, 10u128))
+		.deposit_asset(All, [0u8; 32])
+		.build();
+	assert_eq!(
+		xcm,
+		Xcm(vec![
+			WithdrawAsset(Asset { id: AssetId(Location::here()), fun: Fungible(100) }.into()),
+			SetHints {
+				hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
+					location: Location::parent()
+				}])
+			},
+			ClearOrigin,
+			PayFees { asset: Asset { id: AssetId(Location::here()), fun: Fungible(10) } },
+			DepositAsset {
+				assets: All.into(),
+				beneficiary: AccountId32 { id: [0u8; 32], network: None }.into()
+			},
+		])
+	);
+}
diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/unpaid_execution_named_fields.rs b/polkadot/xcm/procedural/tests/enum_variants.rs
similarity index 70%
rename from polkadot/xcm/procedural/tests/ui/builder_pattern/unpaid_execution_named_fields.rs
rename to polkadot/xcm/procedural/tests/enum_variants.rs
index bb98d603fd91567406063b72d6133ba87671b4c9..4a5362c1579a1b343090ab02a292fd47f2178eed 100644
--- a/polkadot/xcm/procedural/tests/ui/builder_pattern/unpaid_execution_named_fields.rs
+++ b/polkadot/xcm/procedural/tests/enum_variants.rs
@@ -14,17 +14,20 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
-//! Test error when the `BuyExecution` instruction doesn't take named fields.
+//! Test the struct generated by the `NumVariants` derive macro.
 
-use xcm_procedural::Builder;
+use frame_support::traits::Get;
+use xcm_procedural::NumVariants;
 
-struct Xcm<Call>(pub Vec<Instruction<Call>>);
-
-#[derive(Builder)]
-enum Instruction<Call> {
-    BuyExecution { fees: u128 },
-    UnpaidExecution(u32, u32),
-    Transact { call: Call },
+#[allow(dead_code)]
+#[derive(NumVariants)]
+enum SomeEnum {
+	Variant1,
+	Variant2,
+	Variant3,
 }
 
-fn main() {}
+#[test]
+fn num_variants_works() {
+	assert_eq!(SomeEnumNumVariants::get(), 3);
+}
diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/loads_holding_no_operands.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern/loads_holding_no_operands.stderr
deleted file mode 100644
index 0358a35ad3dd7bb48ddd51b69e4f395642d44edf..0000000000000000000000000000000000000000
--- a/polkadot/xcm/procedural/tests/ui/builder_pattern/loads_holding_no_operands.stderr
+++ /dev/null
@@ -1,6 +0,0 @@
-error: Instructions that load the holding register should take operands
-  --> tests/ui/builder_pattern/loads_holding_no_operands.rs:25:5
-   |
-25 | /     #[builder(loads_holding)]
-26 | |     WithdrawAsset,
-   | |_________________^
diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..c4d711e0d455afc08284e6851c31a6185ad7b725
--- /dev/null
+++ b/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.stderr
@@ -0,0 +1,5 @@
+error: Expected `builder(loads_holding)` or `builder(pays_fees)`
+  --> tests/ui/builder_pattern/unexpected_attribute.rs:25:5
+   |
+25 |     #[builder(funds_holding)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/unpaid_execution_named_fields.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern/unpaid_execution_named_fields.stderr
deleted file mode 100644
index 0a3c0a40a33b0a5224f6b940c1ed18552a15fdf3..0000000000000000000000000000000000000000
--- a/polkadot/xcm/procedural/tests/ui/builder_pattern/unpaid_execution_named_fields.stderr
+++ /dev/null
@@ -1,5 +0,0 @@
-error: UnpaidExecution should have named fields
-  --> tests/ui/builder_pattern/unpaid_execution_named_fields.rs:26:5
-   |
-26 |     UnpaidExecution(u32, u32),
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/polkadot/xcm/src/v4/mod.rs b/polkadot/xcm/src/v4/mod.rs
index 3ae94b6ede8849c4dbe855a494bf804814592721..bbf5ca049228292ec56b51d9d6a737826de52561 100644
--- a/polkadot/xcm/src/v4/mod.rs
+++ b/polkadot/xcm/src/v4/mod.rs
@@ -1435,7 +1435,7 @@ impl<Call: Decode + GetDispatchInfo> TryFrom<NewInstruction<Call>> for Instructi
 			},
 			InitiateTransfer { .. } |
 			PayFees { .. } |
-			SetAssetClaimer { .. } |
+			SetHints { .. } |
 			ExecuteWithOrigin { .. } => {
 				log::debug!(target: "xcm::versions::v5tov4", "`{new_instruction:?}` not supported by v4");
 				return Err(());
diff --git a/polkadot/xcm/src/v5/mod.rs b/polkadot/xcm/src/v5/mod.rs
index 193b82b6c223e3c03089c7490081d8683a5db631..21845d07529efda1b0843ce77a26108eb43a92d6 100644
--- a/polkadot/xcm/src/v5/mod.rs
+++ b/polkadot/xcm/src/v5/mod.rs
@@ -196,6 +196,8 @@ pub mod prelude {
 			AssetInstance::{self, *},
 			Assets, BodyId, BodyPart, Error as XcmError, ExecuteXcm,
 			Fungibility::{self, *},
+			Hint::{self, *},
+			HintNumVariants,
 			Instruction::*,
 			InteriorLocation,
 			Junction::{self, *},
@@ -747,15 +749,6 @@ pub enum Instruction<Call> {
 	/// Errors: None.
 	ClearError,
 
-	/// Set asset claimer for all the trapped assets during the execution.
-	///
-	/// - `location`: The claimer of any assets potentially trapped during the execution of current
-	///   XCM. It can be an arbitrary location, not necessarily the caller or origin.
-	///
-	/// Kind: *Command*
-	///
-	/// Errors: None.
-	SetAssetClaimer { location: Location },
 	/// Create some assets which are being held on behalf of the origin.
 	///
 	/// - `assets`: The assets which are to be claimed. This must match exactly with the assets
@@ -1136,6 +1129,25 @@ pub enum Instruction<Call> {
 	/// Errors:
 	/// - `BadOrigin`
 	ExecuteWithOrigin { descendant_origin: Option<InteriorLocation>, xcm: Xcm<Call> },
+
+	/// Set hints for XCM execution.
+	///
+	/// These hints change the behaviour of the XCM program they are present in.
+	///
+	/// Parameters:
+	///
+	/// - `hints`: A bounded vector of `ExecutionHint`, specifying the different hints that will
+	/// be activated.
+	SetHints { hints: BoundedVec<Hint, HintNumVariants> },
+}
+
+#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Eq, Clone, xcm_procedural::NumVariants)]
+pub enum Hint {
+	/// Set asset claimer for all the trapped assets during the execution.
+	///
+	/// - `location`: The claimer of any assets potentially trapped during the execution of current
+	///   XCM. It can be an arbitrary location, not necessarily the caller or origin.
+	AssetClaimer { location: Location },
 }
 
 impl<Call> Xcm<Call> {
@@ -1184,7 +1196,7 @@ impl<Call> Instruction<Call> {
 			SetErrorHandler(xcm) => SetErrorHandler(xcm.into()),
 			SetAppendix(xcm) => SetAppendix(xcm.into()),
 			ClearError => ClearError,
-			SetAssetClaimer { location } => SetAssetClaimer { location },
+			SetHints { hints } => SetHints { hints },
 			ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
 			Trap(code) => Trap(code),
 			SubscribeVersion { query_id, max_response_weight } =>
@@ -1259,7 +1271,7 @@ impl<Call, W: XcmWeightInfo<Call>> GetWeight<W> for Instruction<Call> {
 			SetErrorHandler(xcm) => W::set_error_handler(xcm),
 			SetAppendix(xcm) => W::set_appendix(xcm),
 			ClearError => W::clear_error(),
-			SetAssetClaimer { location } => W::set_asset_claimer(location),
+			SetHints { hints } => W::set_hints(hints),
 			ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
 			Trap(code) => W::trap(code),
 			SubscribeVersion { query_id, max_response_weight } =>
diff --git a/polkadot/xcm/xcm-builder/src/barriers.rs b/polkadot/xcm/xcm-builder/src/barriers.rs
index 56a8493ef0ab2f519bd4a34440a234eb12600f12..adba9a3ef79f14a78d84cd914e65ef3b18c33116 100644
--- a/polkadot/xcm/xcm-builder/src/barriers.rs
+++ b/polkadot/xcm/xcm-builder/src/barriers.rs
@@ -95,7 +95,8 @@ impl<T: Contains<Location>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T>
 			})?
 			.skip_inst_while(|inst| {
 				matches!(inst, ClearOrigin | AliasOrigin(..)) ||
-					matches!(inst, DescendOrigin(child) if child != &Here)
+					matches!(inst, DescendOrigin(child) if child != &Here) ||
+					matches!(inst, SetHints { .. })
 			})?
 			.match_next_inst(|inst| match inst {
 				BuyExecution { weight_limit: Limited(ref mut weight), .. }
diff --git a/polkadot/xcm/xcm-builder/src/tests/barriers.rs b/polkadot/xcm/xcm-builder/src/tests/barriers.rs
index cd2b6db66efcfed828139311d482700faaa02484..d8805274d3a56f31debb31a0a01e9939f599ac71 100644
--- a/polkadot/xcm/xcm-builder/src/tests/barriers.rs
+++ b/polkadot/xcm/xcm-builder/src/tests/barriers.rs
@@ -333,6 +333,26 @@ fn allow_paid_should_deprivilege_origin() {
 	assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(30, 30))));
 }
 
+#[test]
+fn allow_paid_should_allow_hints() {
+	AllowPaidFrom::set(vec![Parent.into()]);
+	let fees = (Parent, 1).into();
+
+	let mut paying_message_with_hints = Xcm::<()>(vec![
+		ReserveAssetDeposited((Parent, 100).into()),
+		SetHints { hints: vec![AssetClaimer { location: Location::here() }].try_into().unwrap() },
+		BuyExecution { fees, weight_limit: Limited(Weight::from_parts(30, 30)) },
+		DepositAsset { assets: AllCounted(1).into(), beneficiary: Here.into() },
+	]);
+	let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
+		&Parent.into(),
+		paying_message_with_hints.inner_mut(),
+		Weight::from_parts(30, 30),
+		&mut props(Weight::zero()),
+	);
+	assert_eq!(r, Ok(()));
+}
+
 #[test]
 fn suspension_should_work() {
 	TestSuspender::set_suspended(true);
diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs
index 11fd4e04761f054d251526f88994062f95bc7ba7..64a32f48842006bdad2d9bb64f5438b179264349 100644
--- a/polkadot/xcm/xcm-executor/src/lib.rs
+++ b/polkadot/xcm/xcm-executor/src/lib.rs
@@ -1371,8 +1371,14 @@ impl<Config: config::Config> XcmExecutor<Config> {
 				self.error = None;
 				Ok(())
 			},
-			SetAssetClaimer { location } => {
-				self.asset_claimer = Some(location);
+			SetHints { hints } => {
+				for hint in hints.into_iter() {
+					match hint {
+						AssetClaimer { location } => {
+							self.asset_claimer = Some(location)
+						},
+					}
+				}
 				Ok(())
 			},
 			ClaimAsset { assets, ticket } => {
diff --git a/polkadot/xcm/xcm-executor/src/tests/set_asset_claimer.rs b/polkadot/xcm/xcm-executor/src/tests/set_asset_claimer.rs
index bc504b8db2a296000bcff463bbad5837e8534cd0..cc97e2b3a16eac45494c811c9bf2563baf0731f7 100644
--- a/polkadot/xcm/xcm-executor/src/tests/set_asset_claimer.rs
+++ b/polkadot/xcm/xcm-executor/src/tests/set_asset_claimer.rs
@@ -38,7 +38,7 @@ fn set_asset_claimer() {
 		// if withdrawing fails we're not missing any corner case.
 		.withdraw_asset((Here, 100u128))
 		.clear_origin()
-		.set_asset_claimer(bob.clone())
+		.set_hints(vec![AssetClaimer { location: bob.clone() }])
 		.pay_fees((Here, 10u128)) // 10% destined for fees, not more.
 		.build();
 
@@ -93,7 +93,7 @@ fn trap_then_set_asset_claimer() {
 		.withdraw_asset((Here, 100u128))
 		.clear_origin()
 		.trap(0u64)
-		.set_asset_claimer(bob)
+		.set_hints(vec![AssetClaimer { location: bob }])
 		.pay_fees((Here, 10u128)) // 10% destined for fees, not more.
 		.build();
 
@@ -121,7 +121,7 @@ fn set_asset_claimer_then_trap() {
 		// if withdrawing fails we're not missing any corner case.
 		.withdraw_asset((Here, 100u128))
 		.clear_origin()
-		.set_asset_claimer(bob.clone())
+		.set_hints(vec![AssetClaimer { location: bob.clone() }])
 		.trap(0u64)
 		.pay_fees((Here, 10u128)) // 10% destined for fees, not more.
 		.build();
diff --git a/prdoc/pr_6566.prdoc b/prdoc/pr_6566.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..bbd48b79953876dd4ecbf564718213291515c127
--- /dev/null
+++ b/prdoc/pr_6566.prdoc
@@ -0,0 +1,45 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: XCMv5 - SetHints instruction
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      Implementation of fellowship RFC 107.
+      The new SetHints instruction is a repackaging of SetAssetClaimer that also allows future
+      "hints" which alter the default behaviour of the executor.
+      The AllowTopLevelPaidExecutionFrom barrier allows this instruction between WithdrawAsset and
+      BuyExecution/PayFees to configure things before the actual meat of the program.
+
+crates:
+  - name: asset-hub-rococo-runtime
+    bump: major
+  - name: asset-hub-westend-runtime
+    bump: major
+  - name: bridge-hub-rococo-runtime
+    bump: major
+  - name: bridge-hub-westend-runtime
+    bump: major
+  - name: coretime-rococo-runtime
+    bump: major
+  - name: coretime-westend-runtime
+    bump: major
+  - name: people-rococo-runtime
+    bump: major
+  - name: people-westend-runtime
+    bump: major
+  - name: rococo-runtime
+    bump: major
+  - name: westend-runtime
+    bump: major
+  - name: pallet-xcm-benchmarks
+    bump: major
+  - name: xcm-procedural
+    bump: minor
+  - name: staging-xcm
+    bump: major
+  - name: staging-xcm-builder
+    bump: major
+  - name: staging-xcm-executor
+    bump: major