Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
46164e8
High level architecture for palswap in place, everything compiles
gztensor Dec 15, 2025
48866a9
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Dec 15, 2025
c9aedf6
Use Sam's bigmath crate for exponentiation
gztensor Dec 17, 2025
1bc3092
Adjust fuzzy epsilon
gztensor Dec 17, 2025
81eab8e
Use rayon for fuzzy testing
gztensor Dec 17, 2025
8bdafa7
Cleanup full vs limited range testing
gztensor Dec 17, 2025
b91ca21
Update bigmath, debug current_price, implement adjust_protocol_liquid…
gztensor Dec 18, 2025
f3daa79
Implement delta_in and tests
gztensor Dec 18, 2025
ef71b3c
Merge devnet-ready
gztensor Dec 18, 2025
0fe9842
Update bigmath, add testcases
gztensor Dec 19, 2025
921ace4
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Dec 22, 2025
9292044
Basic swap works
gztensor Dec 22, 2025
30be6ea
Swap tests in progress
gztensor Dec 22, 2025
22019e3
Fix test_clear_protocol_liquidity_green_path
gztensor Dec 23, 2025
9c9b646
Fix test_claim_root_with_run_coinbase
gztensor Dec 23, 2025
a27f1b5
Add swap initialization to interface
gztensor Dec 23, 2025
afa0d45
Fix test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission
gztensor Dec 24, 2025
243df1e
Fix test_mining_emission_distribution_with_no_root_sell
gztensor Dec 24, 2025
d2f9e2b
Fix test_mining_emission_distribution_with_root_sell
gztensor Dec 24, 2025
96402fd
Fix test_pending_emission_start_call_not_done
gztensor Dec 24, 2025
6c55c7b
Remove non-zero delta-out requirement for zero delta-in. Fix test_mig…
gztensor Dec 24, 2025
b8d5ffe
Fix test_add_stake_insufficient_liquidity_one_side_ok
gztensor Dec 26, 2025
1c663f1
Fix dissolve_clears_all_per_subnet_storages
gztensor Dec 26, 2025
25a9e5d
Fix test_large_swap, improve error messaging
gztensor Dec 26, 2025
213a984
Fix test_add_stake_limit_fill_or_kill
gztensor Dec 26, 2025
b755f0e
Fix test_max_amount_add_dynamic
gztensor Dec 26, 2025
26cca0b
Fix test_max_amount_move_dynamic_dynamic
gztensor Dec 26, 2025
7c51712
Re-enable balancer math test cases
gztensor Dec 26, 2025
6ce98e2
Fix test_max_amount_move_dynamic_stable
gztensor Dec 29, 2025
483dcd4
Fix test_max_amount_move_stable_dynamic
gztensor Dec 29, 2025
3f152bf
Fix test_max_amount_remove_dynamic
gztensor Dec 29, 2025
fb9d494
Fix test_stake_into_subnet_low_amount
gztensor Dec 29, 2025
72262b2
Fix test_swap_fees_tao_correctness
gztensor Dec 29, 2025
5fdc9f9
Refactor: Rename ReserveWeights to Balancer
gztensor Dec 29, 2025
1cc1921
Fix test_claim_root_coinbase_distribution
gztensor Dec 30, 2025
be6fd6f
Fix test_adjust_protocol_liquidity_happy
gztensor Dec 30, 2025
1540d30
Disable add_liquidity tests, fix test_swap_initialization
gztensor Dec 30, 2025
e75ee3a
Update bigmath, cleanup balancer math, add missing balancer tests
gztensor Jan 2, 2026
e7b878c
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 2, 2026
49accc1
Remove provided reserve maps, add liquidity calculation method to bal…
gztensor Jan 5, 2026
c4e21e7
Add test for CurrentLiquidity initialization
gztensor Jan 5, 2026
f97e56c
Fix exponentiation in liquidity calculation
gztensor Jan 6, 2026
7634aee
Update to most recent bigmath
gztensor Jan 7, 2026
0bd3c78
local
sam0x17 Jan 7, 2026
9fed25e
Collect protocol fees in adjust_protocol_liquidity and add to reserves
gztensor Jan 8, 2026
ca466df
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 8, 2026
71995f0
fix
sam0x17 Jan 8, 2026
cf5f277
Merge remote-tracking branch 'origin/feat/balancer_swap' into feat/ba…
sam0x17 Jan 8, 2026
b2744c8
Remove todo() macros from position.rs file
gztensor Jan 8, 2026
1dd3e67
Address a todo in test_remove_stake_edge_alpha
gztensor Jan 8, 2026
56c001e
Remove todo macros from liquidity extrinsics, make them return error
gztensor Jan 8, 2026
c2bbe99
Fix clippy (all except SafeInt)
gztensor Jan 8, 2026
244fbfc
Add more test cases for test_convert_deltas, correct order of swap in…
gztensor Jan 9, 2026
8d19821
Disable add/remove/modify liquidity benchmarks
gztensor Jan 9, 2026
80e9cec
Add reminders to removed benchmarks
gztensor Jan 9, 2026
fbb015c
Fix custom lints
gztensor Jan 9, 2026
65bb1c2
Fix zepter ci
gztensor Jan 9, 2026
87fa1dc
Fix benchmarks
gztensor Jan 9, 2026
b021c56
Fix remove_stake_full_limit
gztensor Jan 9, 2026
6723152
Add clippy ignore for SafeInt division
gztensor Jan 12, 2026
8d48806
Add tests for palswap initialization with price in migration
gztensor Jan 12, 2026
55f9bba
Fix price initialization
gztensor Jan 12, 2026
485949d
Fix remove_stake_full_limit benchmark
gztensor Jan 12, 2026
9f5ca77
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 12, 2026
b4ecbc3
Fix remove_stake_full_limit benchmark
gztensor Jan 12, 2026
3b18a2e
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 15, 2026
a3756d9
Add R&D tests for exp_scaled
gztensor Jan 15, 2026
06c2f71
Fix typo
gztensor Jan 16, 2026
3143a79
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 16, 2026
903848b
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 19, 2026
82c1fe7
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 22, 2026
682a4a9
Fix merge issue
gztensor Jan 22, 2026
7fdb0bf
Bump spec version
gztensor Jan 22, 2026
2651ec1
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 26, 2026
3526657
Fix add_liquidity signature
gztensor Jan 26, 2026
77aa4b6
Add logging for errors in disable_lp
gztensor Jan 26, 2026
b0d9c98
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Jan 28, 2026
e1ada50
Add get_base_needed_for_quote method to balancer (for alpha fees)
gztensor Jan 29, 2026
84dc940
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Feb 2, 2026
0729e36
Spec bump
gztensor Feb 2, 2026
a68189d
Balancer documentation
gztensor Feb 2, 2026
2a6e807
More balancer docs
gztensor Feb 2, 2026
a67c9e7
Add current_alpha_price_all swap RPC, add slippage to sim_swap_tao_fo…
gztensor Feb 3, 2026
fb0017f
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Feb 3, 2026
f1b090d
Remove unused user liquidity code
gztensor Feb 3, 2026
7e8dc3a
Cleanup test_claim_root_coinbase_distribution test
gztensor Feb 3, 2026
4e21da2
More user liquidity cleanup
gztensor Feb 3, 2026
a8579be
Add freeze struct to TickIndex and PositionId
gztensor Feb 3, 2026
4c0f1f1
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Feb 4, 2026
ab43e6b
Fix build errors
gztensor Feb 4, 2026
7809143
Merge remote-tracking branch 'origin/devnet-ready' into feat/balancer…
sam0x17 Feb 4, 2026
def5b9d
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Feb 5, 2026
b8559b0
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Feb 6, 2026
c8b352f
Fix subnet_buyback benchmark
gztensor Feb 6, 2026
5bdbd93
Merge branch 'devnet-ready' into feat/balancer_swap
gztensor Feb 12, 2026
c09f6a6
Spec bump
gztensor Feb 12, 2026
e859f2f
Update subtensor pallet weights
gztensor Feb 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 201 additions & 11 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're using Sam's bigmath crate now to handle high precision exponents.

Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default
pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false }
procedural-fork = { path = "support/procedural-fork", default-features = false }
safe-math = { path = "primitives/safe-math", default-features = false }
safe-bigmath = { package = "safe-bigmath", default-features = false, git = "https://github.com/sam0x17/safe-bigmath" }
share-pool = { path = "primitives/share-pool", default-features = false }
subtensor-macros = { path = "support/macros", default-features = false }
subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" }
Expand Down
4 changes: 2 additions & 2 deletions chain-extensions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use pallet_subtensor_proxy as pallet_proxy;
use pallet_subtensor_proxy::WeightInfo;
use sp_runtime::{DispatchError, Weight, traits::StaticLookup};
use sp_std::marker::PhantomData;
use substrate_fixed::types::U96F32;
use substrate_fixed::types::U64F64;
use subtensor_runtime_common::{AlphaCurrency, NetUid, ProxyType, TaoCurrency};
use subtensor_swap_interface::SwapHandler;

Expand Down Expand Up @@ -520,7 +520,7 @@ where
netuid.into(),
);

let price = current_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000));
let price = current_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000));
let price: u64 = price.saturating_to_num();

let encoded_result = price.encode();
Expand Down
2 changes: 0 additions & 2 deletions chain-extensions/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,6 @@ impl pallet_subtensor::Config for Test {
parameter_types! {
pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap");
pub const SwapMaxFeeRate: u16 = 10000; // 15.26%
pub const SwapMaxPositions: u32 = 100;
pub const SwapMinimumLiquidity: u64 = 1_000;
pub const SwapMinimumReserve: NonZeroU64 = NonZeroU64::new(100).unwrap();
}
Expand All @@ -429,7 +428,6 @@ impl pallet_subtensor_swap::Config for Test {
type TaoReserve = TaoCurrencyReserve<Self>;
type AlphaReserve = AlphaCurrencyReserve<Self>;
type MaxFeeRate = SwapMaxFeeRate;
type MaxPositions = SwapMaxPositions;
type MinimumLiquidity = SwapMinimumLiquidity;
type MinimumReserve = SwapMinimumReserve;
type WeightInfo = ();
Expand Down
4 changes: 2 additions & 2 deletions chain-extensions/src/tests.rs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed U96F32 to U64F64 for representation of price. F64 has more fractional bits and prices are virtually never greater than 1000 anyway, which is way below u64::MAX.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use pallet_subtensor::DefaultMinStake;
use sp_core::Get;
use sp_core::U256;
use sp_runtime::DispatchError;
use substrate_fixed::types::U96F32;
use substrate_fixed::types::U64F64;
use subtensor_runtime_common::{AlphaCurrency, Currency as CurrencyTrait, NetUid, TaoCurrency};
use subtensor_swap_interface::SwapHandler;

Expand Down Expand Up @@ -985,7 +985,7 @@ fn get_alpha_price_returns_encoded_price() {
<pallet_subtensor_swap::Pallet<mock::Test> as SwapHandler>::current_alpha_price(
netuid.into(),
);
let expected_price_scaled = expected_price.saturating_mul(U96F32::from_num(1_000_000_000));
let expected_price_scaled = expected_price.saturating_mul(U64F64::from_num(1_000_000_000));
let expected_price_u64: u64 = expected_price_scaled.saturating_to_num();

let mut env = MockEnv::new(FunctionId::GetAlphaPriceV1, caller, netuid.encode());
Expand Down
2 changes: 0 additions & 2 deletions pallets/admin-utils/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,6 @@ impl pallet_balances::Config for Test {
parameter_types! {
pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap");
pub const SwapMaxFeeRate: u16 = 10000; // 15.26%
pub const SwapMaxPositions: u32 = 100;
pub const SwapMinimumLiquidity: u64 = 1_000;
pub const SwapMinimumReserve: NonZeroU64 = NonZeroU64::new(1_000_000).unwrap();
}
Expand All @@ -334,7 +333,6 @@ impl pallet_subtensor_swap::Config for Test {
type TaoReserve = pallet_subtensor::TaoCurrencyReserve<Self>;
type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve<Self>;
type MaxFeeRate = SwapMaxFeeRate;
type MaxPositions = SwapMaxPositions;
type MinimumLiquidity = SwapMinimumLiquidity;
type MinimumReserve = SwapMinimumReserve;
type WeightInfo = ();
Expand Down
68 changes: 46 additions & 22 deletions pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use sp_runtime::{
};
use sp_std::collections::btree_set::BTreeSet;
use sp_std::vec;
use substrate_fixed::types::U64F64;
use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency};
use subtensor_swap_interface::SwapHandler;

#[benchmarks(
where
Expand Down Expand Up @@ -66,6 +68,8 @@ mod pallet_benchmarks {
Subtensor::<T>::set_max_registrations_per_block(netuid, 4096);
Subtensor::<T>::set_target_registrations_per_interval(netuid, 4096);
Subtensor::<T>::set_commit_reveal_weights_enabled(netuid, false);
SubnetTAO::<T>::insert(netuid, TaoCurrency::from(1_000_000_000_000_u64));
SubnetAlphaIn::<T>::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000_u64));

let mut seed: u32 = 1;
let mut dests = Vec::new();
Expand Down Expand Up @@ -729,13 +733,12 @@ mod pallet_benchmarks {
let coldkey: T::AccountId = account("Test", 0, seed);
let hotkey: T::AccountId = account("Alice", 0, seed);

let amount = 900_000_000_000;
let limit = TaoCurrency::from(6_000_000_000);
let amount_to_be_staked = TaoCurrency::from(44_000_000_000);
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), amount);
let initial_balance = 900_000_000_000;
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), initial_balance);

let tao_reserve = TaoCurrency::from(150_000_000_000);
let alpha_in = AlphaCurrency::from(100_000_000_000);
// Price = 0.01
let tao_reserve = TaoCurrency::from(1_000_000_000_000);
let alpha_in = AlphaCurrency::from(100_000_000_000_000);
SubnetTAO::<T>::insert(netuid, tao_reserve);
SubnetAlphaIn::<T>::insert(netuid, alpha_in);

Expand All @@ -745,14 +748,23 @@ mod pallet_benchmarks {
hotkey.clone()
));

// Read current price and set limit price 0.1% higher, which is certainly getting hit
// by swapping 100 TAO
let current_price = T::SwapInterface::current_alpha_price(netuid);
let limit = current_price
.saturating_mul(U64F64::saturating_from_num(1_001_000_000))
.saturating_to_num::<u64>();
let amount_to_be_staked = TaoCurrency::from(100_000_000_000);

// Allow partial (worst case)
#[extrinsic_call]
_(
RawOrigin::Signed(coldkey.clone()),
hotkey,
netuid,
amount_to_be_staked,
limit,
false,
limit.into(),
true,
);
}

Expand Down Expand Up @@ -829,9 +841,9 @@ mod pallet_benchmarks {
let hotkey: T::AccountId = account("Alice", 0, seed);
Subtensor::<T>::set_burn(netuid, 1.into());

let limit = TaoCurrency::from(1_000_000_000);
let tao_reserve = TaoCurrency::from(150_000_000_000);
let alpha_in = AlphaCurrency::from(100_000_000_000);
// Price = 0.01
let tao_reserve = TaoCurrency::from(1_000_000_000_000);
let alpha_in = AlphaCurrency::from(100_000_000_000_000);
SubnetTAO::<T>::insert(netuid, tao_reserve);
SubnetAlphaIn::<T>::insert(netuid, alpha_in);

Expand All @@ -854,7 +866,13 @@ mod pallet_benchmarks {
u64_staked_amt.into()
));

let amount_unstaked = AlphaCurrency::from(30_000_000_000);
// Read current price and set limit price 0.01% lower, which is certainly getting hit
// by swapping 100 Alpha
let current_price = T::SwapInterface::current_alpha_price(netuid);
let limit = current_price
.saturating_mul(U64F64::saturating_from_num(999_900_000))
.saturating_to_num::<u64>();
let amount_unstaked = AlphaCurrency::from(100_000_000_000);

// Remove stake limit for benchmark
StakingOperationRateLimiter::<T>::remove((hotkey.clone(), coldkey.clone(), netuid));
Expand All @@ -865,8 +883,8 @@ mod pallet_benchmarks {
hotkey.clone(),
netuid,
amount_unstaked,
limit,
false,
limit.into(),
true,
);
}

Expand Down Expand Up @@ -1320,8 +1338,9 @@ mod pallet_benchmarks {
let hotkey: T::AccountId = account("Alice", 0, seed);
Subtensor::<T>::set_burn(netuid, 1.into());

SubnetTAO::<T>::insert(netuid, TaoCurrency::from(150_000_000_000));
SubnetAlphaIn::<T>::insert(netuid, AlphaCurrency::from(100_000_000_000));
// Price = 0.01
SubnetTAO::<T>::insert(netuid, TaoCurrency::from(1_000_000_000_000));
SubnetAlphaIn::<T>::insert(netuid, AlphaCurrency::from(100_000_000_000_000));

Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), 1000000u32.into());

Expand Down Expand Up @@ -1368,9 +1387,8 @@ mod pallet_benchmarks {
let hotkey: T::AccountId = account("Alice", 0, seed);
Subtensor::<T>::set_burn(netuid, 1.into());

let limit = TaoCurrency::from(1_000_000_000);
let tao_reserve = TaoCurrency::from(150_000_000_000);
let alpha_in = AlphaCurrency::from(100_000_000_000);
let tao_reserve = TaoCurrency::from(1_000_000_000_000);
let alpha_in = AlphaCurrency::from(100_000_000_000_000);
SubnetTAO::<T>::insert(netuid, tao_reserve);
SubnetAlphaIn::<T>::insert(netuid, alpha_in);

Expand All @@ -1383,7 +1401,13 @@ mod pallet_benchmarks {
hotkey.clone()
));

let u64_staked_amt = 100_000_000_000;
// Read current price and set limit price 50% lower, which is not getting hit
// by swapping 1 TAO
let current_price = T::SwapInterface::current_alpha_price(netuid);
let limit = current_price
.saturating_mul(U64F64::saturating_from_num(500_000_000))
.saturating_to_num::<u64>();
let u64_staked_amt = 1_000_000_000;
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), u64_staked_amt);

assert_ok!(Subtensor::<T>::add_stake(
Expand All @@ -1400,7 +1424,7 @@ mod pallet_benchmarks {
RawOrigin::Signed(coldkey.clone()),
hotkey.clone(),
netuid,
Some(limit),
Some(limit.into()),
);
}

Expand Down Expand Up @@ -1730,7 +1754,7 @@ mod pallet_benchmarks {

Subtensor::<T>::init_new_network(netuid, tempo);
SubtokenEnabled::<T>::insert(netuid, true);
Subtensor::<T>::set_burn(netuid, 1.into());
Subtensor::<T>::set_burn(netuid, 1000.into());
Subtensor::<T>::set_network_registration_allowed(netuid, true);
Subtensor::<T>::set_max_allowed_uids(netuid, 4096);

Expand Down
84 changes: 82 additions & 2 deletions pallets/subtensor/src/coinbase/block_emission.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,90 @@
use super::*;
use frame_support::traits::Get;
use safe_math::*;
use substrate_fixed::{transcendental::log2, types::I96F32};
use subtensor_runtime_common::TaoCurrency;
use substrate_fixed::{
transcendental::log2,
types::{I96F32, U64F64},
};
use subtensor_runtime_common::{NetUid, TaoCurrency};
use subtensor_swap_interface::SwapHandler;

impl<T: Config> Pallet<T> {
/// Calculates the dynamic TAO emission for a given subnet.
///
/// This function determines the three terms tao_in, alpha_in, alpha_out
/// which are consecutively, 1) the amount of tao injected into the pool
/// 2) the amount of alpha injected into the pool, and 3) the amount of alpha
/// left to be distributed towards miners/validators/owners per block.
///
/// # Arguments
/// * `netuid` - The unique identifier of the subnet.
/// * `tao_emission` - The amount of tao to distribute for this subnet.
/// * `alpha_block_emission` - The maximum alpha emission allowed for the block.
///
/// # Returns
/// * `(u64, u64, u64)` - A tuple containing:
/// - `tao_in_emission`: The adjusted TAO emission always lower or equal to tao_emission
/// - `alpha_in_emission`: The adjusted alpha emission amount to be added into the pool.
/// - `alpha_out_emission`: The remaining alpha emission after adjustments to be distributed to miners/validators.
///
/// The algorithm ensures that the pool injection of tao_in_emission, alpha_in_emission does not effect the pool price
/// It also ensures that the total amount of alpha_in_emission + alpha_out_emission sum to 2 * alpha_block_emission
/// It also ensures that 1 < alpha_out_emission < 2 * alpha_block_emission and 0 < alpha_in_emission < alpha_block_emission.
pub fn get_dynamic_tao_emission(
netuid: NetUid,
tao_emission: u64,
alpha_block_emission: u64,
) -> (u64, u64, u64) {
// Init terms.
let mut tao_in_emission: U64F64 = U64F64::saturating_from_num(tao_emission);
let float_alpha_block_emission: U64F64 = U64F64::saturating_from_num(alpha_block_emission);

// Get alpha price for subnet.
let alpha_price = T::SwapInterface::current_alpha_price(netuid.into());
log::debug!("{netuid:?} - alpha_price: {alpha_price:?}");

// Get initial alpha_in
let mut alpha_in_emission: U64F64 = U64F64::saturating_from_num(tao_emission)
.checked_div(alpha_price)
.unwrap_or(float_alpha_block_emission);

// Check if we are emitting too much alpha_in
if alpha_in_emission >= float_alpha_block_emission {
log::debug!(
"{netuid:?} - alpha_in_emission: {alpha_in_emission:?} > alpha_block_emission: {float_alpha_block_emission:?}"
);

// Scale down tao_in
// tao_in_emission = alpha_price.saturating_mul(float_alpha_block_emission);

// Set to max alpha_block_emission
alpha_in_emission = float_alpha_block_emission;
}

// Avoid rounding errors.
let zero = U64F64::saturating_from_num(0);
let one = U64F64::saturating_from_num(1);
if tao_in_emission < one || alpha_in_emission < one {
alpha_in_emission = zero;
tao_in_emission = zero;
}

// Set Alpha in emission.
let alpha_out_emission = float_alpha_block_emission;

// Log results.
log::debug!("{netuid:?} - tao_in_emission: {tao_in_emission:?}");
log::debug!("{netuid:?} - alpha_in_emission: {alpha_in_emission:?}");
log::debug!("{netuid:?} - alpha_out_emission: {alpha_out_emission:?}");

// Return result.
(
tao_in_emission.saturating_to_num::<u64>(),
alpha_in_emission.saturating_to_num::<u64>(),
alpha_out_emission.saturating_to_num::<u64>(),
)
}

/// Calculates the block emission based on the total issuance.
///
/// This function computes the block emission by applying a logarithmic function
Expand Down
2 changes: 0 additions & 2 deletions pallets/subtensor/src/coinbase/root.rs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love cleaning.

Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@ impl<T: Config> Pallet<T> {
Self::finalize_all_subnet_root_dividends(netuid);

// --- Perform the cleanup before removing the network.
T::SwapInterface::dissolve_all_liquidity_providers(netuid)?;
Self::destroy_alpha_in_out_stakes(netuid)?;
T::SwapInterface::clear_protocol_liquidity(netuid)?;
T::CommitmentsInterface::purge_netuid(netuid);
Expand Down Expand Up @@ -300,7 +299,6 @@ impl<T: Config> Pallet<T> {
SubnetMovingPrice::<T>::remove(netuid);
SubnetTaoFlow::<T>::remove(netuid);
SubnetEmaTaoFlow::<T>::remove(netuid);
SubnetTaoProvided::<T>::remove(netuid);

// --- 13. Token / mechanism / registration toggles.
TokenSymbol::<T>::remove(netuid);
Expand Down
12 changes: 8 additions & 4 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adjust_protocol_liquidity returns the actual adjustment that needs to be made to reserves, which includes previously collected tao and alpha fees.

When tao fees are charged, the tao is coming from user balance to FeesTao map, not accounted for in SubnetTAO, so it needs to be added here.

When alpha fees are charged, the alpha is coming from user's stake, and is also not accounted for in SubnetAlphaIn.

Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ impl<T: Config> Pallet<T> {
let tao_to_swap_with: TaoCurrency =
tou64!(excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))).into();

T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i);
let (actual_injected_tao, actual_injected_alpha) =
T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i);

if tao_to_swap_with > TaoCurrency::ZERO {
let buy_swap_result = Self::swap_tao_for_alpha(
Expand All @@ -87,15 +88,17 @@ impl<T: Config> Pallet<T> {
AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0))));
SubnetAlphaInEmission::<T>::insert(*netuid_i, alpha_in_i);
SubnetAlphaIn::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(alpha_in_i);
// Reserves also received fees in addition to alpha_in_i
*total = total.saturating_add(actual_injected_alpha);
});

// Inject TAO in.
let injected_tao: TaoCurrency =
tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into();
SubnetTaoInEmission::<T>::insert(*netuid_i, injected_tao);
SubnetTAO::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(injected_tao);
// Reserves also received fees in addition to injected_tao
*total = total.saturating_add(actual_injected_tao);
});
TotalStake::<T>::mutate(|total| {
*total = total.saturating_add(injected_tao);
Expand Down Expand Up @@ -140,7 +143,8 @@ impl<T: Config> Pallet<T> {
log::debug!("alpha_emission_i: {alpha_emission_i:?}");

// Get subnet price.
let price_i: U96F32 = T::SwapInterface::current_alpha_price(netuid_i.into());
let price_i: U96F32 =
U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid_i.into()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any risk in this cast? I assume not because Greg is the type master.

Copy link
Contributor Author

@gztensor gztensor Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The U64F64 -> U96F32 conversion only exists here because this PR changes the price from being U96F32 to U64F64: Less digits in its integer part and more digits in its fractional part. Nonetheless, for the purpose on run_coinbase it stays as it was before: U96F32. The risk is loss of lower 32 binary digits of fractional part of the price. The part of price below 0.000000000232831 is thrown away for calculating emissions. I don't think it's a big deal, tbh. But this is not introduced by this PR. It was always like this: We just never maintained the prices so precisely.

log::debug!("price_i: {price_i:?}");

let mut tao_in_i: U96F32 = tao_emission_i;
Expand Down
Loading
Loading