diff --git a/Cargo.lock b/Cargo.lock index 98681c384..b833e3a86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6378,7 +6378,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "411.0.0" +version = "412.0.0" dependencies = [ "alloy-primitives 0.7.7", "alloy-sol-types 0.7.7", @@ -11283,7 +11283,7 @@ dependencies = [ [[package]] name = "pallet-omnipool" -version = "7.3.1" +version = "7.4.0" dependencies = [ "bitflags 1.3.2", "frame-benchmarking", @@ -11878,7 +11878,7 @@ dependencies = [ [[package]] name = "pallet-stableswap" -version = "7.3.0" +version = "7.4.0" dependencies = [ "bitflags 1.3.2", "frame-benchmarking", diff --git a/pallets/omnipool/Cargo.toml b/pallets/omnipool/Cargo.toml index 063996a7e..da2406eb0 100644 --- a/pallets/omnipool/Cargo.toml +++ b/pallets/omnipool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool" -version = "7.3.1" +version = "7.4.0" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 14610a66e..17c179598 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -95,7 +95,6 @@ use hydradx_traits::fee::GetDynamicFee; use hydradx_traits::registry::Inspect as RegistryInspect; use orml_traits::MultiCurrency; use pallet_broadcast::types::{Asset, Destination, ExecutionType, Fee}; -#[cfg(any(feature = "try-runtime", test))] use primitive_types::U256; use scale_info::TypeInfo; use sp_runtime::traits::{AccountIdConversion, AtLeast32BitUnsigned, One}; @@ -455,6 +454,8 @@ pub mod pallet { ProtocolFeeNotConsumed, /// Slip fee configuration exceeds the allowed maximum (50%). MaxSlipFeeTooHigh, + /// Invariant check failed inside a trade or liquidity operation. + InvariantError, } #[pallet::call] @@ -879,6 +880,9 @@ pub mod pallet { let asset_in_state = Self::load_asset_state(asset_in)?; let asset_out_state = Self::load_asset_state(asset_out)?; + let balance_one = + Self::hub_balance_excluding_swap_assets(asset_in, &asset_in_state, asset_out, &asset_out_state)?; + ensure!( Self::allow_assets(&asset_in_state, &asset_out_state), Error::::NotAllowed @@ -1062,11 +1066,11 @@ pub mod pallet { pallet_broadcast::Pallet::::remove_from_context()?; - #[cfg(any(feature = "try-runtime", test))] Self::ensure_trade_invariant( (asset_in, asset_in_state, new_asset_in_state), (asset_out, asset_out_state, new_asset_out_state), - ); + balance_one, + )?; Ok(()) } @@ -1122,6 +1126,9 @@ pub mod pallet { let asset_in_state = Self::load_asset_state(asset_in)?; let asset_out_state = Self::load_asset_state(asset_out)?; + let balance_one = + Self::hub_balance_excluding_swap_assets(asset_in, &asset_in_state, asset_out, &asset_out_state)?; + ensure!( Self::allow_assets(&asset_in_state, &asset_out_state), Error::::NotAllowed @@ -1309,11 +1316,11 @@ pub mod pallet { pallet_broadcast::Pallet::::remove_from_context()?; - #[cfg(any(feature = "try-runtime", test))] Self::ensure_trade_invariant( (asset_in, asset_in_state, new_asset_in_state), (asset_out, asset_out_state, new_asset_out_state), - ); + balance_one, + )?; Ok(()) } @@ -2483,8 +2490,7 @@ impl Pallet { T::OmnipoolHooks::on_liquidity_changed(origin, info)?; - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_liquidity_invariant((asset, asset_state, new_asset_state)); + Self::ensure_liquidity_invariant((asset, asset_state, new_asset_state))?; Ok(instance_id) } @@ -2645,86 +2651,118 @@ impl Pallet { T::OmnipoolHooks::on_liquidity_changed(origin, info)?; - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_liquidity_invariant((asset_id, asset_state, new_asset_state)); + Self::ensure_liquidity_invariant((asset_id, asset_state, new_asset_state))?; Ok(*state_changes.asset.delta_reserve) } - #[cfg(any(feature = "try-runtime", test))] + fn hub_balance_excluding_swap_assets( + asset_in: T::AssetId, + asset_in_state: &AssetReserveState, + asset_out: T::AssetId, + asset_out_state: &AssetReserveState, + ) -> Result { + let hub_supply = T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); + let mut balance = hub_supply + .checked_sub(asset_in_state.hub_reserve) + .ok_or(Error::::InvariantError)? + .checked_sub(asset_out_state.hub_reserve) + .ok_or(Error::::InvariantError)?; + let hdx_id = T::HdxAssetId::get(); + if hdx_id != asset_in && hdx_id != asset_out { + let hdx_state = Self::load_asset_state(hdx_id)?; + balance = balance + .checked_sub(hdx_state.hub_reserve) + .ok_or(Error::::InvariantError)?; + } + Ok(balance) + } + fn ensure_trade_invariant( asset_in: (T::AssetId, AssetReserveState, AssetReserveState), asset_out: (T::AssetId, AssetReserveState, AssetReserveState), - ) { - let new_in_state = asset_in.2; - let new_out_state = asset_out.2; - - let old_in_state = asset_in.1; - let old_out_state = asset_out.1; - debug_assert!(new_in_state.reserve > old_in_state.reserve); - debug_assert!(new_out_state.reserve < old_out_state.reserve); - - let in_new_reserve = U256::from(new_in_state.reserve); - let in_new_hub_reserve = U256::from(new_in_state.hub_reserve); - let in_old_reserve = U256::from(old_in_state.reserve); - let in_old_hub_reserve = U256::from(old_in_state.hub_reserve); - - let rq = in_old_reserve.checked_mul(in_old_hub_reserve).unwrap(); - let rq_plus = in_new_reserve.checked_mul(in_new_hub_reserve).unwrap(); - debug_assert!( - rq_plus >= rq, - "Asset IN trade invariant, {new_in_state:?}, {old_in_state:?}", - ); - - //Ensure Hub reserve in protocol account is equal to sum of all subpool reserves - let hub_reserve = T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); - let subpool_hub_reserve: Balance = >::iter().fold(0, |acc, v| acc + v.1.hub_reserve); - debug_assert_eq!(hub_reserve, subpool_hub_reserve, "Total Hub reserve invariant"); - - /* - let out_new_reserve = U256::from(new_out_state.reserve); - let out_new_hub_reserve = U256::from(new_out_state.hub_reserve); - let out_old_reserve = U256::from(old_out_state.reserve); - let out_old_hub_reserve = U256::from(old_out_state.hub_reserve); - - let left = rq + sp_std::cmp::max(in_new_reserve, in_new_hub_reserve); - let right = rq_plus; - assert!(left >= right, "Asset IN margin {:?} >= {:?}", left,right); - - let rq = out_old_reserve.checked_mul(out_old_hub_reserve).unwrap(); - let rq_plus = out_new_reserve.checked_mul(out_new_hub_reserve).unwrap(); - assert!(rq_plus >= rq, "Asset OUT trade invariant, {:?}, {:?}", new_out_state, old_out_state); - let left = rq + sp_std::cmp::max(out_new_reserve, out_new_hub_reserve); - let right = rq_plus; - assert!(left >= right, "Asset OUT margin {:?} >= {:?}", left,right); - - */ - } - - #[cfg(any(feature = "try-runtime", test))] - fn ensure_liquidity_invariant(asset: (T::AssetId, AssetReserveState, AssetReserveState)) { - let old_state = asset.1; - let new_state = asset.2; + balance_one: Balance, + ) -> DispatchResult { + let r: DispatchResult = (|| { + let asset_in_id = asset_in.0; + let asset_out_id = asset_out.0; + let old_in_state = &asset_in.1; + let new_in_state = &asset_in.2; + + // Per-asset R·Q monotone on the input side. Uses in-memory values + // (trade-math result) — this is the curve-shape check. + let in_new_reserve = U256::from(new_in_state.reserve); + let in_new_hub_reserve = U256::from(new_in_state.hub_reserve); + let in_old_reserve = U256::from(old_in_state.reserve); + let in_old_hub_reserve = U256::from(old_in_state.hub_reserve); + + let rq_before = in_old_reserve + .checked_mul(in_old_hub_reserve) + .ok_or(Error::::InvariantError)?; + let rq_after = in_new_reserve + .checked_mul(in_new_hub_reserve) + .ok_or(Error::::InvariantError)?; + ensure!(rq_after >= rq_before, Error::::InvariantError); + + let in_store = >::get(asset_in_id).ok_or(Error::::InvariantError)?; + let out_store = >::get(asset_out_id).ok_or(Error::::InvariantError)?; + + let mut balance_two = balance_one + .checked_add(in_store.hub_reserve) + .ok_or(Error::::InvariantError)? + .checked_add(out_store.hub_reserve) + .ok_or(Error::::InvariantError)?; + + let hdx_id = T::HdxAssetId::get(); + if hdx_id != asset_in_id && hdx_id != asset_out_id { + let hdx_store = >::get(hdx_id).ok_or(Error::::InvariantError)?; + balance_two = balance_two + .checked_add(hdx_store.hub_reserve) + .ok_or(Error::::InvariantError)?; + } - let new_reserve = U256::from(new_state.reserve); - let new_hub_reserve = U256::from(new_state.hub_reserve); - let old_reserve = U256::from(old_state.reserve); - let old_hub_reserve = U256::from(old_state.hub_reserve); + let p_after = T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); + ensure!(balance_two == p_after, Error::::InvariantError); - let one = U256::from(1_000_000_000_000u128); + Ok(()) + })(); + debug_assert!(r.is_ok(), "Omnipool trade invariant: {:?}", r); + r + } - let left = new_hub_reserve.saturating_sub(one).checked_mul(old_reserve).unwrap(); - let middle = old_hub_reserve.checked_mul(new_reserve).unwrap(); - let right = new_hub_reserve - .checked_add(one) - .unwrap() - .checked_mul(old_reserve) - .unwrap(); + fn ensure_liquidity_invariant( + asset: (T::AssetId, AssetReserveState, AssetReserveState), + ) -> DispatchResult { + let r: DispatchResult = (|| { + let old_state = &asset.1; + let new_state = &asset.2; + + let new_reserve = U256::from(new_state.reserve); + let new_hub_reserve = U256::from(new_state.hub_reserve); + let old_reserve = U256::from(old_state.reserve); + let old_hub_reserve = U256::from(old_state.hub_reserve); + + let one = U256::from(1_000_000_000_000u128); + + let left = new_hub_reserve + .saturating_sub(one) + .checked_mul(old_reserve) + .ok_or(Error::::InvariantError)?; + let middle = old_hub_reserve + .checked_mul(new_reserve) + .ok_or(Error::::InvariantError)?; + let right = new_hub_reserve + .checked_add(one) + .ok_or(Error::::InvariantError)? + .checked_mul(old_reserve) + .ok_or(Error::::InvariantError)?; + + ensure!(left <= middle, Error::::InvariantError); + ensure!(middle <= right, Error::::InvariantError); - debug_assert!(left <= middle, "Add liquidity first part"); - debug_assert!( - middle <= right, - "Add liquidity second part - {left:?} <= {middle:?} <= {right:?}", - ); + Ok(()) + })(); + debug_assert!(r.is_ok(), "Omnipool liquidity invariant: {:?}", r); + r } } diff --git a/pallets/stableswap/Cargo.toml b/pallets/stableswap/Cargo.toml index 8de5ff294..f8d84e56a 100644 --- a/pallets/stableswap/Cargo.toml +++ b/pallets/stableswap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-stableswap" -version = "7.3.0" +version = "7.4.0" description = "AMM for correlated assets" authors = ["GalacticCouncil"] edition = "2021" diff --git a/pallets/stableswap/src/lib.rs b/pallets/stableswap/src/lib.rs index e46c25553..58a72b287 100644 --- a/pallets/stableswap/src/lib.rs +++ b/pallets/stableswap/src/lib.rs @@ -74,7 +74,6 @@ use sp_std::num::NonZeroU16; use sp_std::prelude::*; use sp_std::vec; -#[cfg(any(feature = "try-runtime", test))] use sp_runtime::FixedU128; mod trade_execution; @@ -397,6 +396,9 @@ pub mod pallet { /// Trade would result in zero amount in. ZeroAmountIn, + + /// Invariant check failed inside a trade or liquidity operation. + InvariantError, } #[pallet::call] @@ -699,8 +701,7 @@ pub mod pallet { fees, ); - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance); + Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance)?; Ok(()) } @@ -803,8 +804,7 @@ pub mod pallet { }], ); - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_trade_invariant(pool_id, &initial_reserves, pool.fee); + Self::ensure_trade_invariant(pool_id, &initial_reserves, pool.fee)?; Ok(()) } @@ -910,8 +910,7 @@ pub mod pallet { }], ); - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_trade_invariant(pool_id, &initial_reserves, pool.fee); + Self::ensure_trade_invariant(pool_id, &initial_reserves, pool.fee)?; Ok(()) } @@ -1079,8 +1078,7 @@ pub mod pallet { fee: Balance::zero(), }); - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance); + Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance)?; Ok(()) } @@ -1615,8 +1613,7 @@ impl Pallet { // All done and updated. let's call the on_liquidity_changed hook. Self::call_on_liquidity_change_hook(pool_id, &initial_reserves, share_issuance)?; - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_add_liquidity_invariant(pool_id, &initial_reserves, share_issuance); + Self::ensure_add_liquidity_invariant(pool_id, &initial_reserves, share_issuance)?; Self::deposit_event(Event::LiquidityAdded { pool_id, @@ -1713,8 +1710,7 @@ impl Pallet { //All done and update. let's call the on_liquidity_changed hook. Self::call_on_liquidity_change_hook(pool_id, &initial_reserves, share_issuance)?; - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_add_liquidity_invariant(pool_id, &initial_reserves, share_issuance); + Self::ensure_add_liquidity_invariant(pool_id, &initial_reserves, share_issuance)?; pallet_broadcast::Pallet::::deposit_trade_event( who.clone(), @@ -1813,8 +1809,7 @@ impl Pallet { }], ); - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance); + Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance)?; Ok(amount) } @@ -1909,8 +1904,7 @@ impl Pallet { fee: Balance::zero(), }); - #[cfg(any(feature = "try-runtime", test))] - Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance); + Self::ensure_remove_liquidity_invariant(pool_id, &initial_reserves, share_issuance)?; Ok(()) } @@ -2005,102 +1999,108 @@ impl Pallet { Ok(state) } - #[cfg(any(feature = "try-runtime", test))] fn ensure_add_liquidity_invariant( pool_id: T::AssetId, initial_reserves: &[AssetReserve], initial_issuance: Balance, - ) { - let pool = Pools::::get(pool_id).unwrap(); - let (_, asset_pegs) = Self::get_updated_pegs(pool_id, &pool).unwrap(); - let final_reserves = pool.reserves_with_decimals::(&Self::pool_account(pool_id)).unwrap(); - debug_assert_ne!( - initial_reserves.iter().map(|v| v.amount).collect::>(), - final_reserves.iter().map(|v| v.amount).collect::>(), - "Reserves have not changed" - ); - let amplification = Self::get_amplification(&pool); - let initial_d = - hydra_dx_math::stableswap::calculate_d::(initial_reserves, amplification, &asset_pegs) - .unwrap(); - let final_d = - hydra_dx_math::stableswap::calculate_d::(&final_reserves, amplification, &asset_pegs) - .unwrap(); - assert!( - final_d >= initial_d, - "Add liquidity Invariant broken: D+ is less than initial D; {initial_d:?} <= {final_d:?}", - ); - if initial_issuance.is_zero() { - return; - } - let current_share_issuance = T::Currency::total_issuance(pool_id); - let initial_r = FixedU128::from_rational(initial_d, initial_issuance); - let final_r = FixedU128::from_rational(final_d, current_share_issuance); - assert!( - final_r >= initial_r, - "Add liquidity Invariant broken: R+ is less than initial R; {initial_r:?} <= {final_r:?}", - ); + ) -> DispatchResult { + let r: DispatchResult = (|| { + let pool = Pools::::get(pool_id).ok_or(Error::::InvariantError)?; + let (_, asset_pegs) = Self::get_updated_pegs(pool_id, &pool).map_err(|_| Error::::InvariantError)?; + let final_reserves = pool + .reserves_with_decimals::(&Self::pool_account(pool_id)) + .ok_or(Error::::InvariantError)?; + debug_assert_ne!( + initial_reserves.iter().map(|v| v.amount).collect::>(), + final_reserves.iter().map(|v| v.amount).collect::>(), + "Reserves have not changed" + ); + let amplification = Self::get_amplification(&pool); + let initial_d = + hydra_dx_math::stableswap::calculate_d::(initial_reserves, amplification, &asset_pegs) + .ok_or(Error::::InvariantError)?; + let final_d = + hydra_dx_math::stableswap::calculate_d::(&final_reserves, amplification, &asset_pegs) + .ok_or(Error::::InvariantError)?; + ensure!(final_d >= initial_d, Error::::InvariantError); + if initial_issuance.is_zero() { + return Ok(()); + } + let current_share_issuance = T::Currency::total_issuance(pool_id); + ensure!(!current_share_issuance.is_zero(), Error::::InvariantError); + let initial_r = FixedU128::from_rational(initial_d, initial_issuance); + let final_r = FixedU128::from_rational(final_d, current_share_issuance); + ensure!(final_r >= initial_r, Error::::InvariantError); + Ok(()) + })(); + debug_assert!(r.is_ok(), "Stableswap add_liquidity invariant: {:?}", r); + r } - #[cfg(any(feature = "try-runtime", test))] fn ensure_remove_liquidity_invariant( pool_id: T::AssetId, initial_reserves: &[AssetReserve], initial_issuance: Balance, - ) { - let Some(pool) = Pools::::get(pool_id) else { - return; - }; - let (_, asset_pegs) = Self::get_updated_pegs(pool_id, &pool).unwrap(); - let final_reserves = pool.reserves_with_decimals::(&Self::pool_account(pool_id)).unwrap(); - debug_assert_ne!( - initial_reserves.iter().map(|v| v.amount).collect::>(), - final_reserves.iter().map(|v| v.amount).collect::>(), - "Reserves have not changed" - ); - let amplification = Self::get_amplification(&pool); - let initial_d = - hydra_dx_math::stableswap::calculate_d::(initial_reserves, amplification, &asset_pegs) - .unwrap(); - let final_d = - hydra_dx_math::stableswap::calculate_d::(&final_reserves, amplification, &asset_pegs) - .unwrap(); - assert!( - final_d <= initial_d, - "Remove liquidity Invariant broken: D+ is more than initial D; {initial_d:?} >= {final_d:?}", - ); - let current_share_issuance = T::Currency::total_issuance(pool_id); - if current_share_issuance.is_zero() { - return; - } - let initial_r = FixedU128::from_rational(initial_d, initial_issuance); - let final_r = FixedU128::from_rational(final_d, current_share_issuance); - assert!( - final_r >= initial_r, - "Remove liquidity Invariant broken: R+ is less than initial R; {initial_r:?} <= {final_r:?}", - ); + ) -> DispatchResult { + let r: DispatchResult = (|| { + let Some(pool) = Pools::::get(pool_id) else { + return Ok(()); + }; + let (_, asset_pegs) = Self::get_updated_pegs(pool_id, &pool).map_err(|_| Error::::InvariantError)?; + let final_reserves = pool + .reserves_with_decimals::(&Self::pool_account(pool_id)) + .ok_or(Error::::InvariantError)?; + debug_assert_ne!( + initial_reserves.iter().map(|v| v.amount).collect::>(), + final_reserves.iter().map(|v| v.amount).collect::>(), + "Reserves have not changed" + ); + let amplification = Self::get_amplification(&pool); + let initial_d = + hydra_dx_math::stableswap::calculate_d::(initial_reserves, amplification, &asset_pegs) + .ok_or(Error::::InvariantError)?; + let final_d = + hydra_dx_math::stableswap::calculate_d::(&final_reserves, amplification, &asset_pegs) + .ok_or(Error::::InvariantError)?; + ensure!(final_d <= initial_d, Error::::InvariantError); + let current_share_issuance = T::Currency::total_issuance(pool_id); + if current_share_issuance.is_zero() { + return Ok(()); + } + ensure!(!initial_issuance.is_zero(), Error::::InvariantError); + let initial_r = FixedU128::from_rational(initial_d, initial_issuance); + let final_r = FixedU128::from_rational(final_d, current_share_issuance); + ensure!(final_r >= initial_r, Error::::InvariantError); + Ok(()) + })(); + debug_assert!(r.is_ok(), "Stableswap remove_liquidity invariant: {:?}", r); + r } - #[cfg(any(feature = "try-runtime", test))] - fn ensure_trade_invariant(pool_id: T::AssetId, initial_reserves: &[AssetReserve], _fee: Permill) { - let pool = Pools::::get(pool_id).unwrap(); - let (_, asset_pegs) = Self::get_updated_pegs(pool_id, &pool).unwrap(); - let final_reserves = pool.reserves_with_decimals::(&Self::pool_account(pool_id)).unwrap(); - debug_assert_ne!( - initial_reserves.iter().map(|v| v.amount).collect::>(), - final_reserves.iter().map(|v| v.amount).collect::>(), - "Reserves are not changed" - ); - let amplification = Self::get_amplification(&pool); - let initial_d = - hydra_dx_math::stableswap::calculate_d::(initial_reserves, amplification, &asset_pegs) - .unwrap(); - let final_d = - hydra_dx_math::stableswap::calculate_d::(&final_reserves, amplification, &asset_pegs) - .unwrap(); - assert!( - final_d >= initial_d, - "Trade Invariant broken: D+ is less than initial D; {initial_d:?} <= {final_d:?}", - ); + + fn ensure_trade_invariant(pool_id: T::AssetId, initial_reserves: &[AssetReserve], _fee: Permill) -> DispatchResult { + let r: DispatchResult = (|| { + let pool = Pools::::get(pool_id).ok_or(Error::::InvariantError)?; + let (_, asset_pegs) = Self::get_updated_pegs(pool_id, &pool).map_err(|_| Error::::InvariantError)?; + let final_reserves = pool + .reserves_with_decimals::(&Self::pool_account(pool_id)) + .ok_or(Error::::InvariantError)?; + debug_assert_ne!( + initial_reserves.iter().map(|v| v.amount).collect::>(), + final_reserves.iter().map(|v| v.amount).collect::>(), + "Reserves are not changed" + ); + let amplification = Self::get_amplification(&pool); + let initial_d = + hydra_dx_math::stableswap::calculate_d::(initial_reserves, amplification, &asset_pegs) + .ok_or(Error::::InvariantError)?; + let final_d = + hydra_dx_math::stableswap::calculate_d::(&final_reserves, amplification, &asset_pegs) + .ok_or(Error::::InvariantError)?; + ensure!(final_d >= initial_d, Error::::InvariantError); + Ok(()) + })(); + debug_assert!(r.is_ok(), "Stableswap trade invariant: {:?}", r); + r } } diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 221ce37d1..24fc4f13b 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "411.0.0" +version = "412.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index ee4df8830..8f640c355 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("hydradx"), impl_name: Cow::Borrowed("hydradx"), authoring_version: 1, - spec_version: 411, + spec_version: 412, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1,