From 906fa9e51306635245a22e03160d1c761fae6cc3 Mon Sep 17 00:00:00 2001
From: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Date: Mon, 9 Dec 2024 00:02:38 +0100
Subject: [PATCH] XCM V5 - SetHints instruction (#6566)

Last feature we wanted for V5, changing `SetAssetClaimer` to be just one
of many possible "hints" that you can specify at the beginning of your
program to change its behaviour.

This makes it easier to add new hints in the future and have barriers
accept them.

---------

Co-authored-by: GitHub Action <action@github.com>
---
 Cargo.lock                                    |   1 +
 .../pallets/inbound-queue/src/test.rs         |   4 +-
 .../src/tests/set_asset_claimer.rs            |   4 +-
 .../asset-hub-rococo/src/weights/xcm/mod.rs   |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../asset-hub-westend/src/weights/xcm/mod.rs  |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../bridge-hub-rococo/src/weights/xcm/mod.rs  |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../bridge-hub-westend/src/weights/xcm/mod.rs |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../coretime-rococo/src/weights/xcm/mod.rs    |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../coretime-westend/src/weights/xcm/mod.rs   |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../people-rococo/src/weights/xcm/mod.rs      |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../people-westend/src/weights/xcm/mod.rs     |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../runtime/rococo/src/weights/xcm/mod.rs     |  13 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../runtime/westend/src/weights/xcm/mod.rs    |  15 +-
 .../xcm/pallet_xcm_benchmarks_generic.rs      |   2 +-
 .../src/generic/benchmarking.rs               |   8 +-
 polkadot/xcm/procedural/Cargo.toml            |   2 +
 .../xcm/procedural/src/builder_pattern.rs     | 394 ++++++++++--------
 .../enum_variants.rs}                         |  36 +-
 polkadot/xcm/procedural/src/lib.rs            |   9 +
 .../xcm/procedural/tests/builder_pattern.rs   |  59 +++
 ...ution_named_fields.rs => enum_variants.rs} |  23 +-
 .../loads_holding_no_operands.stderr          |   6 -
 .../unexpected_attribute.stderr               |   5 +
 .../unpaid_execution_named_fields.stderr      |   5 -
 polkadot/xcm/src/v4/mod.rs                    |   2 +-
 polkadot/xcm/src/v5/mod.rs                    |  34 +-
 polkadot/xcm/xcm-builder/src/barriers.rs      |   3 +-
 .../xcm/xcm-builder/src/tests/barriers.rs     |  20 +
 polkadot/xcm/xcm-executor/src/lib.rs          |  10 +-
 .../src/tests/set_asset_claimer.rs            |   6 +-
 prdoc/pr_6566.prdoc                           |  45 ++
 40 files changed, 559 insertions(+), 269 deletions(-)
 rename polkadot/xcm/procedural/{tests/ui/builder_pattern/loads_holding_no_operands.rs => src/enum_variants.rs} (51%)
 rename polkadot/xcm/procedural/tests/{ui/builder_pattern/unpaid_execution_named_fields.rs => enum_variants.rs} (70%)
 delete mode 100644 polkadot/xcm/procedural/tests/ui/builder_pattern/loads_holding_no_operands.stderr
 create mode 100644 polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.stderr
 delete mode 100644 polkadot/xcm/procedural/tests/ui/builder_pattern/unpaid_execution_named_fields.stderr
 create mode 100644 prdoc/pr_6566.prdoc

diff --git a/Cargo.lock b/Cargo.lock
index dad578ba0c1..cee1e2ce741 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 5386b845f2e..1e0bd8acc92 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 544b0536052..bc00106b47c 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 74f56403740..ccf473484ca 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 b69c136b29d..d48debef94c 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 ff99f1242b2..a0e9705ff01 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 52869412311..0ec2741c049 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 e5c6f493d6d..efc2798999b 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 bac73e0e056..daf22190a42 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 939b1c7a287..15a1dae09d9 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 6434f6206fb..03cbaa866ad 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 2c4a97601c6..dc21e2ea117 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 d207c09ffcd..cdcba6134bf 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 906088a1df8..29466b3718c 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 fb6e4631736..6c6d3cf8c52 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 47008a2943e..d55198f60a0 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 6aac6119e7e..caa91650734 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 27fd499ebba..915a499cb77 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 36400f2c1e6..ad2cde22a07 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 16f51a77891..eb27e5c5a89 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 e5915a7986b..2dc8880c832 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 60265445334..d2691c998d9 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 076744a5975..dfc02fd20bc 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 285322891c6..431c7a5f137 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 3167766158f..88ed3c94ddf 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 b65290332af..34b89f13422 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 070f0be6bac..f9f2d9e1567 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 9971fdceb69..0dd270286f6 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 4202309bf3f..3915621916d 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 bb98d603fd9..4a5362c1579 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 0358a35ad3d..00000000000
--- 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 00000000000..c4d711e0d45
--- /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 0a3c0a40a33..00000000000
--- 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 3ae94b6ede8..bbf5ca04922 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 193b82b6c22..21845d07529 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 56a8493ef0a..adba9a3ef79 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 cd2b6db66ef..d8805274d3a 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 11fd4e04761..64a32f48842 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 bc504b8db2a..cc97e2b3a16 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 00000000000..bbd48b79953
--- /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
-- 
GitLab