-
Notifications
You must be signed in to change notification settings - Fork 317
Handle balancer misbalance on injection #2758
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: devnet-ready
Are you sure you want to change the base?
Changes from all commits
799af38
0da9a76
11096e6
02982dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -79,41 +79,112 @@ impl<T: Config> Pallet<T> { | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Returns actually added Tao and Alpha, which may be zero in case | ||
| /// of a high disbalance | ||
| /// Adjusts balancer weights with minted TAO and alpha liquidity to | ||
| /// maintain price. | ||
| /// | ||
| /// If weights cannot be adjusted (get pushed out of range), the excess TAO | ||
| /// and/or Alpha are added to reservoirs and an attempt to use them will be made | ||
| /// later. | ||
| /// | ||
| /// Returns: | ||
| /// 1. price-active TAO delta to add to `SubnetTAO` | ||
| /// 2. TAO delta to materialize by the caller | ||
| /// 3. price-active Alpha delta to add to `SubnetAlphaIn` | ||
| /// 4. Alpha delta to materialize by the caller | ||
| /// | ||
| /// Amounts that would push weights out of range are materialized but left in | ||
| /// per-subnet reservoirs for a later balancer update. | ||
| /// | ||
| /// Reservoir amounts may be included in the balancer update, but they are not | ||
| /// returned for materialization because they were already materialized when | ||
| /// first stored. | ||
| pub(super) fn adjust_protocol_liquidity( | ||
| netuid: NetUid, | ||
| tao_delta: TaoBalance, | ||
| alpha_delta: AlphaBalance, | ||
| ) -> (TaoBalance, AlphaBalance) { | ||
| ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance) { | ||
| // Get reserves | ||
| let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); | ||
| let tao_reserve = T::TaoReserve::reserve(netuid.into()); | ||
| let mut balancer = SwapBalancer::<T>::get(netuid); | ||
| let balancer = SwapBalancer::<T>::get(netuid); | ||
|
|
||
| let pending_tao = BalancerTaoReservoir::<T>::get(netuid).saturating_add(tao_delta); | ||
| let pending_alpha = BalancerAlphaReservoir::<T>::get(netuid).saturating_add(alpha_delta); | ||
|
|
||
| if let Some(new_balancer) = Self::try_update_balancer( | ||
| &balancer, | ||
| tao_reserve, | ||
| alpha_reserve, | ||
| pending_tao, | ||
| pending_alpha, | ||
| ) { | ||
| BalancerTaoReservoir::<T>::insert(netuid, TaoBalance::ZERO); | ||
| BalancerAlphaReservoir::<T>::insert(netuid, AlphaBalance::ZERO); | ||
| SwapBalancer::<T>::insert(netuid, new_balancer); | ||
| return (pending_tao, tao_delta, pending_alpha, alpha_delta); | ||
| } | ||
|
|
||
| // Update weights and log errors if they go out of range | ||
| if balancer | ||
| .update_weights_for_added_liquidity( | ||
| u64::from(tao_reserve), | ||
| u64::from(alpha_reserve), | ||
| u64::from(tao_delta), | ||
| u64::from(alpha_delta), | ||
| ) | ||
| .is_err() | ||
| { | ||
| if let Some(new_balancer) = Self::try_update_balancer( | ||
| &balancer, | ||
| tao_reserve, | ||
| alpha_reserve, | ||
| TaoBalance::ZERO, | ||
| pending_alpha, | ||
| ) { | ||
| BalancerTaoReservoir::<T>::insert(netuid, pending_tao); | ||
| BalancerAlphaReservoir::<T>::insert(netuid, AlphaBalance::ZERO); | ||
| SwapBalancer::<T>::insert(netuid, new_balancer); | ||
| return (TaoBalance::ZERO, tao_delta, pending_alpha, alpha_delta); | ||
| } | ||
|
|
||
| if let Some(new_balancer) = Self::try_update_balancer( | ||
| &balancer, | ||
| tao_reserve, | ||
| alpha_reserve, | ||
| pending_tao, | ||
| AlphaBalance::ZERO, | ||
| ) { | ||
| BalancerTaoReservoir::<T>::insert(netuid, TaoBalance::ZERO); | ||
| BalancerAlphaReservoir::<T>::insert(netuid, pending_alpha); | ||
| SwapBalancer::<T>::insert(netuid, new_balancer); | ||
| return (pending_tao, tao_delta, AlphaBalance::ZERO, alpha_delta); | ||
| } | ||
|
|
||
| BalancerTaoReservoir::<T>::insert(netuid, pending_tao); | ||
| BalancerAlphaReservoir::<T>::insert(netuid, pending_alpha); | ||
| if pending_tao > TaoBalance::ZERO || pending_alpha > AlphaBalance::ZERO { | ||
| log::warn!( | ||
| "Reserves are out of range for emission: netuid = {}, tao = {}, alpha = {}, tao_delta = {}, alpha_delta = {}", | ||
| "Reserves are out of range for emission: netuid = {}, tao = {}, alpha = {}, tao_delta = {}, alpha_delta = {}, tao_reservoir = {}, alpha_reservoir = {}", | ||
| netuid, | ||
| tao_reserve, | ||
| alpha_reserve, | ||
| tao_delta, | ||
| alpha_delta | ||
| alpha_delta, | ||
| pending_tao, | ||
| pending_alpha | ||
| ); | ||
| (TaoBalance::ZERO, AlphaBalance::ZERO) | ||
| } else { | ||
| SwapBalancer::<T>::insert(netuid, balancer); | ||
| (tao_delta, alpha_delta) | ||
| } | ||
|
|
||
| (TaoBalance::ZERO, tao_delta, AlphaBalance::ZERO, alpha_delta) | ||
| } | ||
|
|
||
| fn try_update_balancer( | ||
| balancer: &Balancer, | ||
| tao_reserve: TaoBalance, | ||
| alpha_reserve: AlphaBalance, | ||
| tao_delta: TaoBalance, | ||
| alpha_delta: AlphaBalance, | ||
| ) -> Option<Balancer> { | ||
| let mut new_balancer = balancer.clone(); | ||
| new_balancer | ||
| .update_weights_for_added_liquidity( | ||
| u64::from(tao_reserve), | ||
| u64::from(alpha_reserve), | ||
| u64::from(tao_delta), | ||
| u64::from(alpha_delta), | ||
| ) | ||
| .ok()?; | ||
| Some(new_balancer) | ||
| } | ||
|
|
||
| /// Executes a token swap on the specified subnet. | ||
|
|
@@ -276,6 +347,8 @@ impl<T: Config> Pallet<T> { | |
|
|
||
| FeeRate::<T>::remove(netuid); | ||
| SwapBalancer::<T>::remove(netuid); | ||
| BalancerTaoReservoir::<T>::remove(netuid); | ||
|
Comment on lines
349
to
+350
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] Reservoir cleanup drops materialized liquidity from accounting
|
||
| BalancerAlphaReservoir::<T>::remove(netuid); | ||
|
|
||
| log::debug!( | ||
| "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" | ||
|
|
@@ -390,7 +463,7 @@ impl<T: Config> SwapHandler for Pallet<T> { | |
| netuid: NetUid, | ||
| tao_delta: TaoBalance, | ||
| alpha_delta: AlphaBalance, | ||
| ) -> (TaoBalance, AlphaBalance) { | ||
| ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance) { | ||
| Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta) | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[HIGH] Reservoir alpha is minted outside the subnet issuance counters
When
adjust_protocol_liquidityreservoirs alpha, it returnsalpha_deltaasalpha_to_materializeeven ifprice_active_alphais zero, and this call mints it immediately. The emitted alpha then lives only inBalancerAlphaReservoir; it is not added toSubnetAlphaIn/SubnetAlphaOut, whileget_alpha_issuance()still computes issuance from those two counters. Until the reservoir later activates, subsequent alpha emissions are calculated from an understated subnet issuance, and if the subnet is dissolved firstdo_clear_protocol_liquidity()just removesBalancerAlphaReservoirwithout recycling/burning the already minted alpha-assets issuance.Fix this by making the reservoir's accounting match its materialization semantics: either count reservoir alpha in subnet issuance and retire it during liquidity cleanup, or treat alpha reservoirs as unmaterialized pending emission and mint only when the alpha becomes price-active. Add coverage for both “alpha held in reservoir across blocks affects issuance correctly” and “dissolve with non-zero
BalancerAlphaReservoirdoes not leave minted alpha issuance behind.”