Skip to content

Commit b73d7c8

Browse files
committed
fix leasing to distribute alpha only
1 parent 2af7993 commit b73d7c8

File tree

2 files changed

+100
-80
lines changed

2 files changed

+100
-80
lines changed

pallets/subtensor/src/subnets/leasing.rs

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ use frame_system::pallet_prelude::*;
2424
use sp_core::blake2_256;
2525
use sp_runtime::{Percent, traits::TrailingZeroInput};
2626
use substrate_fixed::types::U64F64;
27-
use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency};
28-
use subtensor_swap_interface::SwapHandler;
27+
use subtensor_runtime_common::{AlphaCurrency, NetUid};
2928

3029
pub type LeaseId = u32;
3130

@@ -130,6 +129,9 @@ impl<T: Config> Pallet<T> {
130129
);
131130
SubnetUidToLeaseId::<T>::insert(netuid, lease_id);
132131

132+
// The lease take should be 0% to allow all contributors to receive dividends entirely.
133+
Self::delegate_hotkey(&lease_hotkey, 0);
134+
133135
// Get all the contributions to the crowdloan except for the beneficiary
134136
// because its share will be computed as the dividends are distributed
135137
let contributions = pallet_crowdloan::Contributions::<T>::iter_prefix(crowdloan_id)
@@ -249,9 +251,8 @@ impl<T: Config> Pallet<T> {
249251

250252
/// Hook used when the subnet owner's cut is distributed to split the amount into dividends
251253
/// for the contributors and the beneficiary in shares relative to their initial contributions.
252-
///
253-
/// It will ensure the subnet has enough alpha in its liquidity pool before swapping it to tao to be distributed,
254-
/// and if not enough liquidity is available, it will accumulate the dividends for later distribution.
254+
/// It accumulates dividends to be distributed later when the interval for distribution is reached.
255+
/// Distribution is made in alpha and stake to the contributor coldkey and lease hotkey.
255256
pub fn distribute_leased_network_dividends(lease_id: LeaseId, owner_cut_alpha: AlphaCurrency) {
256257
// Ensure the lease exists
257258
let Some(lease) = SubnetLeases::<T>::get(lease_id) else {
@@ -290,55 +291,48 @@ impl<T: Config> Pallet<T> {
290291
return;
291292
}
292293

293-
// Ensure there is enough liquidity to unstake the contributors cut
294-
if let Err(err) = Self::validate_remove_stake(
295-
&lease.coldkey,
296-
&lease.hotkey,
297-
lease.netuid,
298-
total_contributors_cut_alpha,
299-
total_contributors_cut_alpha,
300-
false,
301-
) {
302-
log::debug!("Couldn't distributing dividends for lease {lease_id}: {err:?}");
303-
AccumulatedLeaseDividends::<T>::set(lease_id, total_contributors_cut_alpha);
304-
return;
305-
}
306-
307-
// Unstake the contributors cut from the subnet as tao to the lease coldkey
308-
let tao_unstaked = match Self::unstake_from_subnet(
309-
&lease.hotkey,
310-
&lease.coldkey,
311-
lease.netuid,
312-
total_contributors_cut_alpha,
313-
T::SwapInterface::min_price(),
314-
false,
315-
) {
316-
Ok(tao_unstaked) => tao_unstaked,
317-
Err(err) => {
318-
log::debug!("Couldn't distributing dividends for lease {lease_id}: {err:?}");
319-
AccumulatedLeaseDividends::<T>::set(lease_id, total_contributors_cut_alpha);
320-
return;
294+
// We use a storage layer to ensure the distribution is atomic.
295+
if let Err(err) = frame_support::storage::with_storage_layer(|| {
296+
let mut alpha_distributed = AlphaCurrency::ZERO;
297+
298+
// Distribute the contributors cut to the contributors and accumulate the alpha
299+
// distributed so far to obtain how much alpha is left to distribute to the beneficiary
300+
for (contributor, share) in SubnetLeaseShares::<T>::iter_prefix(lease_id) {
301+
let alpha_for_contributor = share
302+
.saturating_mul(U64F64::from(total_contributors_cut_alpha.to_u64()))
303+
.ceil()
304+
.saturating_to_num::<u64>();
305+
Self::transfer_stake_within_subnet(
306+
&lease.coldkey,
307+
&lease.hotkey,
308+
&contributor,
309+
&lease.hotkey,
310+
lease.netuid,
311+
alpha_for_contributor.into(),
312+
)?;
313+
alpha_distributed = alpha_distributed.saturating_add(alpha_for_contributor.into());
321314
}
322-
};
323315

324-
// Distribute the contributors cut to the contributors and accumulate the tao
325-
// distributed so far to obtain how much tao is left to distribute to the beneficiary
326-
let mut tao_distributed = TaoCurrency::ZERO;
327-
for (contributor, share) in SubnetLeaseShares::<T>::iter_prefix(lease_id) {
328-
let tao_for_contributor = share
329-
.saturating_mul(U64F64::from(tao_unstaked.to_u64()))
330-
.floor()
331-
.saturating_to_num::<u64>();
332-
Self::add_balance_to_coldkey_account(&contributor, tao_for_contributor);
333-
tao_distributed = tao_distributed.saturating_add(tao_for_contributor.into());
334-
}
316+
// Distribute the leftover alpha to the beneficiary
317+
let beneficiary_cut_alpha =
318+
total_contributors_cut_alpha.saturating_sub(alpha_distributed);
319+
Self::transfer_stake_within_subnet(
320+
&lease.coldkey,
321+
&lease.hotkey,
322+
&lease.beneficiary,
323+
&lease.hotkey,
324+
lease.netuid,
325+
beneficiary_cut_alpha.into(),
326+
)?;
335327

336-
// Distribute the leftover tao to the beneficiary
337-
let beneficiary_cut_tao = tao_unstaked.saturating_sub(tao_distributed);
338-
Self::add_balance_to_coldkey_account(&lease.beneficiary, beneficiary_cut_tao.into());
328+
// Reset the accumulated dividends
329+
AccumulatedLeaseDividends::<T>::insert(lease_id, AlphaCurrency::ZERO);
339330

340-
// Reset the accumulated dividends
341-
AccumulatedLeaseDividends::<T>::insert(lease_id, AlphaCurrency::ZERO);
331+
Ok::<(), DispatchError>(())
332+
}) {
333+
log::debug!("Couldn't distributing dividends for lease {lease_id}: {err:?}");
334+
AccumulatedLeaseDividends::<T>::set(lease_id, total_contributors_cut_alpha);
335+
};
342336
}
343337

344338
fn lease_coldkey(lease_id: LeaseId) -> Result<T::AccountId, DispatchError> {

pallets/subtensor/src/tests/leasing.rs

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -502,54 +502,80 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() {
502502
// Setup the correct block to distribute dividends
503503
run_to_block(<Test as Config>::LeaseDividendsDistributionInterval::get() as u64);
504504

505-
// Get the initial subnet tao after stake and ensure all contributor
506-
// balances are in initial state
507-
let subnet_tao_before = SubnetTAO::<Test>::get(lease.netuid);
508-
let contributor1_balance_before = SubtensorModule::get_coldkey_balance(&contributions[0].0);
509-
let contributor2_balance_before = SubtensorModule::get_coldkey_balance(&contributions[1].0);
510-
let beneficiary_balance_before = SubtensorModule::get_coldkey_balance(&beneficiary);
505+
// Get the initial alpha for the contributors and beneficiary and ensure they are zero
506+
let contributor1_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
507+
&lease.hotkey,
508+
&contributions[0].0,
509+
lease.netuid,
510+
);
511+
assert_eq!(contributor1_alpha_before, AlphaCurrency::ZERO);
512+
let contributor2_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
513+
&lease.hotkey,
514+
&contributions[1].0,
515+
lease.netuid,
516+
);
517+
assert_eq!(contributor2_alpha_before, AlphaCurrency::ZERO);
518+
let beneficiary_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
519+
&lease.hotkey,
520+
&beneficiary,
521+
lease.netuid,
522+
);
523+
assert_eq!(beneficiary_alpha_before, AlphaCurrency::ZERO);
511524

512525
// Setup some previously accumulated dividends
513-
let accumulated_dividends = AlphaCurrency::from(5_000_000);
526+
let accumulated_dividends = AlphaCurrency::from(10_000_000_000);
514527
AccumulatedLeaseDividends::<Test>::insert(lease_id, accumulated_dividends);
515528

516529
// Distribute the dividends
517-
let owner_cut_alpha = AlphaCurrency::from(5_000_000);
530+
let owner_cut_alpha = AlphaCurrency::from(5_000_000_000);
518531
SubtensorModule::distribute_leased_network_dividends(lease_id, owner_cut_alpha);
519532

520533
// Ensure the dividends were distributed correctly relative to their shares
521-
let distributed_tao = subnet_tao_before - SubnetTAO::<Test>::get(lease.netuid);
522-
let contributor1_balance_delta = SubtensorModule::get_coldkey_balance(&contributions[0].0)
523-
.saturating_sub(contributor1_balance_before);
524-
let contributor2_balance_delta = SubtensorModule::get_coldkey_balance(&contributions[1].0)
525-
.saturating_sub(contributor2_balance_before);
526-
let beneficiary_balance_delta = SubtensorModule::get_coldkey_balance(&beneficiary)
527-
.saturating_sub(beneficiary_balance_before);
528-
534+
let distributed_alpha =
535+
accumulated_dividends + emissions_share.mul_ceil(owner_cut_alpha.to_u64()).into();
536+
let contributor1_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
537+
&lease.hotkey,
538+
&contributions[0].0,
539+
lease.netuid,
540+
)
541+
.saturating_sub(contributor1_alpha_before);
542+
let contributor2_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
543+
&lease.hotkey,
544+
&contributions[1].0,
545+
lease.netuid,
546+
)
547+
.saturating_sub(contributor2_alpha_before);
548+
let beneficiary_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
549+
&lease.hotkey,
550+
&beneficiary,
551+
lease.netuid,
552+
)
553+
.saturating_sub(beneficiary_alpha_before);
554+
555+
// What has been distributed should be equal to the sum of all contributors received alpha
529556
assert_eq!(
530-
distributed_tao,
531-
(beneficiary_balance_delta + contributor1_balance_delta + contributor2_balance_delta)
532-
.into()
557+
distributed_alpha,
558+
(beneficiary_alpha_delta + contributor1_alpha_delta + contributor2_alpha_delta).into()
533559
);
534560

535-
let expected_contributor1_balance =
561+
let expected_contributor1_alpha =
536562
SubnetLeaseShares::<Test>::get(lease_id, contributions[0].0)
537-
.saturating_mul(U64F64::from(distributed_tao.to_u64()))
538-
.floor()
563+
.saturating_mul(U64F64::from(distributed_alpha.to_u64()))
564+
.ceil()
539565
.to_num::<u64>();
540-
assert_eq!(contributor1_balance_delta, expected_contributor1_balance);
566+
assert_eq!(contributor1_alpha_delta, expected_contributor1_alpha.into());
541567

542-
let expected_contributor2_balance =
568+
let expected_contributor2_alpha =
543569
SubnetLeaseShares::<Test>::get(lease_id, contributions[1].0)
544-
.saturating_mul(U64F64::from(distributed_tao.to_u64()))
545-
.floor()
570+
.saturating_mul(U64F64::from(distributed_alpha.to_u64()))
571+
.ceil()
546572
.to_num::<u64>();
547-
assert_eq!(contributor2_balance_delta, expected_contributor2_balance);
573+
assert_eq!(contributor2_alpha_delta, expected_contributor2_alpha.into());
548574

549575
// The beneficiary should have received the remaining dividends
550-
let expected_beneficiary_balance = distributed_tao.to_u64()
551-
- (expected_contributor1_balance + expected_contributor2_balance);
552-
assert_eq!(beneficiary_balance_delta, expected_beneficiary_balance);
576+
let expected_beneficiary_alpha = distributed_alpha.to_u64()
577+
- (expected_contributor1_alpha + expected_contributor2_alpha);
578+
assert_eq!(beneficiary_alpha_delta, expected_beneficiary_alpha.into());
553579

554580
// Ensure nothing was accumulated for later distribution
555581
assert_eq!(

0 commit comments

Comments
 (0)