diff --git a/polkadot/xcm/xcm-builder/src/tests.rs b/polkadot/xcm/xcm-builder/src/tests.rs
index 37878f082e09ba02afd2165c1144a665457fcbd1..ceff66a2a828b44abda267ac8e1f9353148759e9 100644
--- a/polkadot/xcm/xcm-builder/src/tests.rs
+++ b/polkadot/xcm/xcm-builder/src/tests.rs
@@ -15,6 +15,7 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use super::{mock::*, *};
+use frame_support::{assert_err, weights::constants::WEIGHT_PER_SECOND};
 use xcm::latest::prelude::*;
 use xcm_executor::{traits::*, Config, XcmExecutor};
 
@@ -383,3 +384,52 @@ fn prepaid_result_of_query_should_get_free_execution() {
 	let r = XcmExecutor::<TestConfig>::execute_xcm(origin.clone(), message.clone(), weight_limit);
 	assert_eq!(r, Outcome::Incomplete(10, XcmError::Barrier));
 }
+
+fn fungible_multi_asset(location: MultiLocation, amount: u128) -> MultiAsset {
+	(AssetId::from(location), Fungibility::Fungible(amount)).into()
+}
+
+#[test]
+fn weight_trader_tuple_should_work() {
+	pub const PARA_1: MultiLocation = X1(Parachain(1));
+	pub const PARA_2: MultiLocation = X1(Parachain(2));
+
+	parameter_types! {
+		pub static HereWeightPrice: (AssetId, u128) = (Here.into(), WEIGHT_PER_SECOND.into());
+		pub static PARA1WeightPrice: (AssetId, u128) = (PARA_1.into(), WEIGHT_PER_SECOND.into());
+	}
+
+	type Traders = (
+		// trader one
+		FixedRateOfFungible<HereWeightPrice, ()>,
+		// trader two
+		FixedRateOfFungible<PARA1WeightPrice, ()>,
+	);
+
+	let mut traders = Traders::new();
+	// trader one buys weight
+	assert_eq!(
+		traders.buy_weight(5, fungible_multi_asset(Here, 10).into()),
+		Ok(fungible_multi_asset(Here, 5).into()),
+	);
+	// trader one refunds
+	assert_eq!(traders.refund_weight(2), Some(fungible_multi_asset(Here, 2)));
+
+	let mut traders = Traders::new();
+	// trader one failed; trader two buys weight
+	assert_eq!(
+		traders.buy_weight(5, fungible_multi_asset(PARA_1, 10).into()),
+		Ok(fungible_multi_asset(PARA_1, 5).into()),
+	);
+	// trader two refunds
+	assert_eq!(traders.refund_weight(2), Some(fungible_multi_asset(PARA_1, 2)));
+
+	let mut traders = Traders::new();
+	// all traders fails
+	assert_err!(
+		traders.buy_weight(5, fungible_multi_asset(PARA_2, 10).into()),
+		XcmError::TooExpensive,
+	);
+	// and no refund
+	assert_eq!(traders.refund_weight(2), None);
+}
diff --git a/polkadot/xcm/xcm-executor/src/traits/weight.rs b/polkadot/xcm/xcm-executor/src/traits/weight.rs
index 8c9e6ec6366d944158a0c6a623730f3e72868afd..8d962c88e5ece8a60ae6e2d37c04ea954eee4f22 100644
--- a/polkadot/xcm/xcm-executor/src/traits/weight.rs
+++ b/polkadot/xcm/xcm-executor/src/traits/weight.rs
@@ -58,6 +58,11 @@ pub trait UniversalWeigher {
 }
 
 /// Charge for weight in order to execute XCM.
+///
+/// A `WeightTrader` may also be put into a tuple, in which case the default behavior of
+/// `buy_weight` and `refund_weight` would be to attempt to call each tuple element's own
+/// implementation of these two functions, in the order of which they appear in the tuple,
+/// returning early when a successful result is returned.
 pub trait WeightTrader: Sized {
 	/// Create a new trader instance.
 	fn new() -> Self;
@@ -76,11 +81,31 @@ pub trait WeightTrader: Sized {
 	}
 }
 
-impl WeightTrader for () {
+#[impl_trait_for_tuples::impl_for_tuples(30)]
+impl WeightTrader for Tuple {
 	fn new() -> Self {
-		()
+		for_tuples!( ( #( Tuple::new() ),* ) )
 	}
-	fn buy_weight(&mut self, _: Weight, _: Assets) -> Result<Assets, Error> {
-		Err(Error::Unimplemented)
+
+	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
+		let mut last_error = None;
+		for_tuples!( #(
+			match Tuple.buy_weight(weight, payment.clone()) {
+				Ok(assets) => return Ok(assets),
+				Err(e) => { last_error = Some(e) }
+			}
+		)* );
+		let last_error = last_error.unwrap_or(Error::TooExpensive);
+		log::trace!(target: "xcm::buy_weight", "last_error: {:?}", last_error);
+		Err(last_error)
+	}
+
+	fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
+		for_tuples!( #(
+			if let Some(asset) = Tuple.refund_weight(weight) {
+				return Some(asset);
+			}
+		)* );
+		None
 	}
 }