From ecdab482776f661ebf36a6b8e72046cb449ee916 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 4 Apr 2024 14:40:21 +0200 Subject: [PATCH] XCM builder pattern improvement - Accept `impl Into` instead of just `T` (#3708) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XCM builder pattern lets you build xcms like so: ```rust let xcm = Xcm::builder() .withdraw_asset((Parent, 100u128).into()) .buy_execution((Parent, 1u128).into()) .deposit_asset(All.into(), AccountId32 { id: [0u8; 32], network: None }.into()) .build(); ``` All the `.into()` become quite annoying to have to write. I accepted `impl Into` instead of `T` in the generated methods from the macro. Now the previous example can be simplified as follows: ```rust let xcm = Xcm::builder() .withdraw_asset((Parent, 100u128)) .buy_execution((Parent, 1u128)) .deposit_asset(All, [0u8; 32]) .build(); ``` --------- Co-authored-by: Bastian Köcher Co-authored-by: command-bot <> Co-authored-by: Adrian Catangiu --- polkadot/xcm/pallet-xcm/src/tests/mod.rs | 5 ++- .../xcm/procedural/src/builder_pattern.rs | 18 +++++++---- .../xcm/procedural/tests/builder_pattern.rs | 14 ++++----- polkadot/xcm/src/v4/location.rs | 7 +++++ prdoc/pr_3708.prdoc | 31 +++++++++++++++++++ 5 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 prdoc/pr_3708.prdoc diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 763d768e154a5..1147635081a41 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -486,9 +486,8 @@ fn claim_assets_works() { let balances = vec![(ALICE, INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { // First trap some assets. - let trapping_program = Xcm::::builder_unsafe() - .withdraw_asset((Here, SEND_AMOUNT).into()) - .build(); + let trapping_program = + Xcm::::builder_unsafe().withdraw_asset((Here, SEND_AMOUNT)).build(); // Even though assets are trapped, the extrinsic returns success. assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index e58c51103497a..0a33d52580fca 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -107,7 +107,8 @@ fn generate_builder_raw_impl(name: &Ident, data_enum: &DataEnum) -> TokenStream2 .collect(); let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty).collect(); quote! { - pub fn #method_name(mut self, #(#arg_names: #arg_types),*) -> Self { + pub fn #method_name(mut self, #(#arg_names: impl Into<#arg_types>),*) -> Self { + #(let #arg_names = #arg_names.into();)* self.instructions.push(#name::::#variant_name(#(#arg_names),*)); self } @@ -117,7 +118,8 @@ fn generate_builder_raw_impl(name: &Ident, data_enum: &DataEnum) -> TokenStream2 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: #arg_types),*) -> Self { + pub fn #method_name(mut self, #(#arg_names: impl Into<#arg_types>),*) -> Self { + #(let #arg_names = #arg_names.into();)* self.instructions.push(#name::::#variant_name { #(#arg_names),* }); self } @@ -188,8 +190,9 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result = fields.unnamed.iter().map(|field| &field.ty).collect(); quote! { #(#docs)* - pub fn #method_name(self, #(#arg_names: #arg_types),*) -> XcmBuilder { + pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder { let mut new_instructions = self.instructions; + #(let #arg_names = #arg_names.into();)* new_instructions.push(#name::::#variant_name(#(#arg_names),*)); XcmBuilder { instructions: new_instructions, @@ -203,8 +206,9 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result = fields.named.iter().map(|field| &field.ty).collect(); quote! { #(#docs)* - pub fn #method_name(self, #(#arg_names: #arg_types),*) -> XcmBuilder { + pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder { let mut new_instructions = self.instructions; + #(let #arg_names = #arg_names.into();)* new_instructions.push(#name::::#variant_name { #(#arg_names),* }); XcmBuilder { instructions: new_instructions, @@ -249,8 +253,9 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result XcmBuilder { + pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder { let mut new_instructions = self.instructions; + #(let #arg_names = #arg_names.into();)* new_instructions.push(#name::::#variant_name { #(#arg_names),* }); XcmBuilder { instructions: new_instructions, @@ -308,8 +313,9 @@ fn generate_builder_unpaid_impl(name: &Ident, data_enum: &DataEnum) -> Result XcmBuilder { #(#docs)* - pub fn #unpaid_execution_method_name(self, #(#arg_names: #arg_types),*) -> XcmBuilder { + pub fn #unpaid_execution_method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder { let mut new_instructions = self.instructions; + #(let #arg_names = #arg_names.into();)* new_instructions.push(#name::::#unpaid_execution_ident { #(#arg_names),* }); XcmBuilder { instructions: new_instructions, diff --git a/polkadot/xcm/procedural/tests/builder_pattern.rs b/polkadot/xcm/procedural/tests/builder_pattern.rs index a9a30611dc019..96b16fb7e4565 100644 --- a/polkadot/xcm/procedural/tests/builder_pattern.rs +++ b/polkadot/xcm/procedural/tests/builder_pattern.rs @@ -22,11 +22,11 @@ use xcm::latest::prelude::*; #[test] fn builder_pattern_works() { let asset: Asset = (Here, 100u128).into(); - let beneficiary: Location = AccountId32 { id: [0u8; 32], network: None }.into(); + let beneficiary: Location = [0u8; 32].into(); let message: Xcm<()> = Xcm::builder() - .receive_teleported_asset(asset.clone().into()) + .receive_teleported_asset(asset.clone()) .buy_execution(asset.clone(), Unlimited) - .deposit_asset(asset.clone().into(), beneficiary.clone()) + .deposit_asset(asset.clone(), beneficiary.clone()) .build(); assert_eq!( message, @@ -53,8 +53,8 @@ fn default_builder_requires_buy_execution() { // To be able to do that, we need to use the explicitly unpaid variant let message: Xcm<()> = Xcm::builder_unpaid() .unpaid_execution(Unlimited, None) - .withdraw_asset(asset.clone().into()) - .deposit_asset(asset.clone().into(), beneficiary.clone()) + .withdraw_asset(asset.clone()) + .deposit_asset(asset.clone(), beneficiary.clone()) .build(); // This works assert_eq!( message, @@ -68,8 +68,8 @@ fn default_builder_requires_buy_execution() { // The other option doesn't have any limits whatsoever, so it should // only be used when you really know what you're doing. let message: Xcm<()> = Xcm::builder_unsafe() - .withdraw_asset(asset.clone().into()) - .deposit_asset(asset.clone().into(), beneficiary.clone()) + .withdraw_asset(asset.clone()) + .deposit_asset(asset.clone(), beneficiary.clone()) .build(); assert_eq!( message, diff --git a/polkadot/xcm/src/v4/location.rs b/polkadot/xcm/src/v4/location.rs index 0e7c69864fa33..9275bfdb94923 100644 --- a/polkadot/xcm/src/v4/location.rs +++ b/polkadot/xcm/src/v4/location.rs @@ -530,6 +530,13 @@ impl> From> for Location { } } +impl From<[u8; 32]> for Location { + fn from(bytes: [u8; 32]) -> Self { + let junction: Junction = bytes.into(); + junction.into() + } +} + xcm_procedural::impl_conversion_functions_for_location_v4!(); #[cfg(test)] diff --git a/prdoc/pr_3708.prdoc b/prdoc/pr_3708.prdoc new file mode 100644 index 0000000000000..6cb0ebd2c7f92 --- /dev/null +++ b/prdoc/pr_3708.prdoc @@ -0,0 +1,31 @@ +# 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: XCM builder pattern automatically converts instruction parameters. + +doc: + - audience: Runtime Dev + description: | + Small quality of life improvement. + Previously, an XCM could be built like this: + ```rust + let xcm = Xcm::builder() + .withdraw_asset((Parent, 100u128).into()) + .buy_execution((Parent, 1u128).into()) + .deposit_asset(All.into(), AccountId32 { id: [0u8; 32], network: None }.into()) + .build(); + ``` + Now, it can be built like this: + ```rust + let xcm = Xcm::builder() + .withdraw_asset((Parent, 100u128)) + .buy_execution((Parent, 1u128)) + .deposit_asset(All, [0u8; 32]) + .build(); + ``` + +crates: +- name: "xcm-procedural" + bump: minor +- name: "staging-xcm" + bump: minor