diff --git a/integration-tests/src/global_withdraw_limit.rs b/integration-tests/src/global_withdraw_limit.rs index f5105deae..396d0bed4 100644 --- a/integration-tests/src/global_withdraw_limit.rs +++ b/integration-tests/src/global_withdraw_limit.rs @@ -1528,3 +1528,49 @@ fn inbound_xcm_incomplete_message_still_accounts_egress() { ); }); } + +#[test] +fn polkadot_xcm_execute_withdraw_external_asset_succeeds_when_oracle_cannot_price_route() { + TestNet::reset(); + Hydra::execute_with(|| { + init_global_withdraw_limit_params(); + + assert_ok!(CircuitBreaker::set_asset_category( + hydradx_runtime::RuntimeOrigin::root(), + DOT, + Some(GlobalAssetCategory::External) + )); + // XCM asset transactor needs a location -> asset id mapping for DOT. + assert_ok!(hydradx_runtime::AssetRegistry::set_location(DOT, DOT_ASSET_LOCATION)); + assert_ok!(hydradx_runtime::MultiTransactionPayment::add_currency( + hydradx_runtime::RuntimeOrigin::root(), + DOT, + FixedU128::from_rational(50, 100), + )); + pallet_transaction_multi_payment::AcceptedCurrencyPrice::::insert( + DOT, + FixedU128::from_rational(50, 100), + ); + + let alice: AccountId = ALICE.into(); + let amount = 10 * UNITS; + let alice_dot_before = Currencies::free_balance(DOT, &alice); + assert!(alice_dot_before >= amount); + + //Act + let message = xcm_message_withdraw_deposit(Location::parent(), amount); + let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute { + message: Box::new(VersionedXcm::from(message)), + max_weight: Weight::from_parts(1_000_000_000_000, 0), + }); + + assert_ok!(call.dispatch(hydradx_runtime::RuntimeOrigin::signed(ALICE.into()))); + + //Assert + assert!( + Currencies::free_balance(DOT, &alice) <= alice_dot_before - amount, + "DOT must have been withdrawn from Alice for the outbound XCM; before={alice_dot_before}, after={}", + Currencies::free_balance(DOT, &alice) + ); + }); +} diff --git a/runtime/hydradx/src/circuit_breaker.rs b/runtime/hydradx/src/circuit_breaker.rs index 72f1718f0..e2736e6be 100644 --- a/runtime/hydradx/src/circuit_breaker.rs +++ b/runtime/hydradx/src/circuit_breaker.rs @@ -7,8 +7,9 @@ use pallet_asset_registry::AssetType; use pallet_circuit_breaker::types::EgressOperationKind; use pallet_circuit_breaker::GlobalAssetCategory; use primitives::Balance; +use sp_runtime::helpers_128bit::multiply_by_rational_with_rounding; use sp_runtime::traits::Convert; -use sp_runtime::DispatchResult; +use sp_runtime::{DispatchResult, FixedPointNumber, FixedU128, Rounding}; use sp_std::marker::PhantomData; pub struct WithdrawLimitHandler(PhantomData); @@ -26,14 +27,17 @@ impl> WithdrawCircuitBreaker::convert(( + ConvertBalance::::convert(( asset_id, ref_currency, amount, )) - .ok_or(pallet_circuit_breaker::Error::::FailedToConvertAsset)?; - - Ok(converted) + .map(|(converted, _)| converted) + .or_else(|| { + let price = MultiTransactionPayment::currency_price(asset_id)?; + multiply_by_rational_with_rounding(amount, FixedU128::DIV, price.into_inner(), Rounding::Up) + }) + .ok_or_else(|| pallet_circuit_breaker::Error::::FailedToConvertAsset.into()) } pub fn global_asset_category(asset_id: AssetId) -> Option {