From 7990808383b70dbcc80f431074d0fb0fafb7395e Mon Sep 17 00:00:00 2001 From: Deepanshu Hooda Date: Wed, 11 Oct 2023 16:15:44 +0530 Subject: [PATCH] add native support for transfer_with_fee --- precompiles/xcm/src/lib.rs | 44 ++++++++++----- precompiles/xcm/src/tests.rs | 105 +++++++++++++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 17 deletions(-) diff --git a/precompiles/xcm/src/lib.rs b/precompiles/xcm/src/lib.rs index caccd9f297..7bbf32f7f8 100644 --- a/precompiles/xcm/src/lib.rs +++ b/precompiles/xcm/src/lib.rs @@ -582,11 +582,11 @@ where // Read call arguments let currency_address = input.read::
()?; - let amount_of_tokens = input + let amount_of_tokens: u128 = input .read::()? .try_into() .map_err(|_| revert("error converting amount_of_tokens, maybe value too large"))?; - let fee = input + let fee: u128 = input .read::()? .try_into() .map_err(|_| revert("can't convert fee"))?; @@ -594,24 +594,42 @@ where let destination = input.read::()?; let weight = input.read::()?; - let asset_id = Runtime::address_to_asset_id(currency_address.into()) - .ok_or(revert("Failed to resolve fee asset id from address"))?; let dest_weight_limit = if weight.is_zero() { WeightLimit::Unlimited } else { WeightLimit::Limited(weight.get_weight()) }; - log::trace!(target: "xcm-precompile::transfer_with_fee", "Raw arguments: currency_address: {:?}, amount_of_tokens: {:?}, destination: {:?}, \ - weight: {:?}, calculated asset_id: {:?}", - currency_address, amount_of_tokens, destination, weight, asset_id); + let call = { + if currency_address == Address::from(NATIVE_ADDRESS) { + log::trace!(target: "xcm-precompile::transfer_with_fee", "Raw arguments: currency_address: {:?} (this is native token), amount_of_tokens: {:?}, destination: {:?}, \ + weight: {:?}, fee {:?}", + currency_address, amount_of_tokens, destination, weight, fee ); - let call = orml_xtokens::Call::::transfer_with_fee { - currency_id: asset_id.into(), - amount: amount_of_tokens, - fee, - dest: Box::new(VersionedMultiLocation::V3(destination)), - dest_weight_limit, + orml_xtokens::Call::::transfer_multiasset_with_fee { + asset: Box::new(VersionedMultiAsset::V3( + (MultiLocation::here(), amount_of_tokens).into(), + )), + fee: Box::new(VersionedMultiAsset::V3((MultiLocation::here(), fee).into())), + dest: Box::new(VersionedMultiLocation::V3(destination)), + dest_weight_limit, + } + } else { + let asset_id = Runtime::address_to_asset_id(currency_address.into()) + .ok_or(revert("Failed to resolve fee asset id from address"))?; + + log::trace!(target: "xcm-precompile::transfer_with_fee", "Raw arguments: currency_address: {:?}, amount_of_tokens: {:?}, destination: {:?}, \ + weight: {:?}, calculated asset_id: {:?}, fee: {:?}", + currency_address, amount_of_tokens, destination, weight, asset_id, fee); + + orml_xtokens::Call::::transfer_with_fee { + currency_id: asset_id.into(), + amount: amount_of_tokens.into(), + fee: fee.into(), + dest: Box::new(VersionedMultiLocation::V3(destination)), + dest_weight_limit, + } + } }; let origin = Some(Runtime::AddressMapping::into_account_id( diff --git a/precompiles/xcm/src/tests.rs b/precompiles/xcm/src/tests.rs index 1521013bbe..c5c08d812a 100644 --- a/precompiles/xcm/src/tests.rs +++ b/precompiles/xcm/src/tests.rs @@ -71,10 +71,59 @@ mod xcm_old_interface_test { .expect_no_logs() .execute_reverts(|output| { let error_string = String::from_utf8_lossy(output); - if error_string.contains("AssetIndexNonExistent") { - return true; - } - false + error_string.contains("AssetIndexNonExistent") + }); + }); + } + + #[test] + fn sanity_checks_for_parameters() { + ExtBuilder::default().build().execute_with(|| { + // parachain id resolution failure + precompiles() + .prepare_test( + TestAccount::Alice, + PRECOMPILE_ADDRESS, + EvmDataWriter::new_with_selector(Action::AssetsWithdrawNative) + .write(vec![Address::from(Runtime::asset_id_to_address(1u128))]) + .write(vec![U256::from(42000u64)]) + .write(H256::repeat_byte(0xF1)) + .write(false) + .write(U256::from(u64::MAX)) // parachain id should be u32 + .write(U256::from(0_u64)) + .build(), + ) + .expect_no_logs() + .execute_reverts(|output| { + output == b"error converting parachain_id, maybe value too large" + }); + + // more than 2 assets can not be sent + precompiles() + .prepare_test( + TestAccount::Alice, + PRECOMPILE_ADDRESS, + EvmDataWriter::new_with_selector(Action::AssetsWithdrawNative) + .write(vec![ + Address::from(H160::repeat_byte(0xF1)), + Address::from(H160::repeat_byte(0xF2)), + Address::from(H160::repeat_byte(0xF3)), + ]) + .write(vec![ + U256::from(42000u64), + U256::from(42000u64), + U256::from(42000u64), + ]) + .write(H256::repeat_byte(0xF1)) + .write(false) + .write(U256::from(1_u64)) // parachain id should be + .write(U256::from(0_u64)) + .build(), + ) + .expect_no_logs() + .execute_reverts(|output| { + let error_string = String::from_utf8_lossy(output); + error_string.contains("Array has more than max items allowed") }); }); } @@ -523,6 +572,54 @@ mod xcm_new_interface_test { }); } + #[test] + fn xtokens_transfer_with_fee_works_for_native_asset() { + let weight = WeightV2::from(3_000_000_000u64, 1024); + let parent_destination = MultiLocation { + parents: 1, + interior: Junctions::X1(Junction::AccountId32 { + network: None, + id: [1u8; 32], + }), + }; + + ExtBuilder::default().build().execute_with(|| { + // sending native token to relay + precompiles() + .prepare_test( + TestAccount::Alice, + PRECOMPILE_ADDRESS, + EvmDataWriter::new_with_selector(Action::XtokensTransferWithFee) + .write(Address::from(NATIVE_ADDRESS)) // zero address by convention + .write(U256::from(42000u64)) + .write(U256::from(50)) + .write(parent_destination) + .write(weight.clone()) + .build(), + ) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + let expected_asset: MultiAsset = MultiAsset { + id: AssetId::Concrete(Here.into()), + fun: Fungibility::Fungible(42000), + }; + let expected_fee: MultiAsset = MultiAsset { + id: AssetId::Concrete(Here.into()), + fun: Fungibility::Fungible(50), + }; + let expected: crate::mock::RuntimeEvent = + mock::RuntimeEvent::Xtokens(XtokensEvent::TransferredMultiAssets { + sender: TestAccount::Alice.into(), + assets: vec![expected_asset.clone(), expected_fee.clone()].into(), + fee: expected_fee, + dest: parent_destination, + }) + .into(); + assert!(events().contains(&expected)); + }); + } + #[test] fn transfer_multiasset_works() { let weight = WeightV2::from(3_000_000_000u64, 1024);