Skip to content

Commit e831393

Browse files
authored
Merge pull request #1996 from opentensor/ckburn
Ckburn
2 parents ede7a35 + 4b05d0c commit e831393

File tree

14 files changed

+200
-20
lines changed

14 files changed

+200
-20
lines changed

evm-tests/src/contracts/alpha.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,5 +315,18 @@ export const IAlphaABI = [
315315
],
316316
"stateMutability": "view",
317317
"type": "function"
318+
},
319+
{
320+
"inputs": [],
321+
"name": "getCKBurn",
322+
"outputs": [
323+
{
324+
"internalType": "uint256",
325+
"name": "",
326+
"type": "uint256"
327+
}
328+
],
329+
"stateMutability": "view",
330+
"type": "function"
318331
}
319332
]

evm-tests/test/alpha.precompile.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { PublicClient } from "viem";
88
import { PolkadotSigner, TypedApi } from "polkadot-api";
99
import { toViemAddress, convertPublicKeyToSs58 } from "../src/address-utils"
1010
import { IAlphaABI, IALPHA_ADDRESS } from "../src/contracts/alpha"
11+
import { u64 } from "@polkadot-api/substrate-bindings";
1112

1213
describe("Test Alpha Precompile", () => {
1314
// init substrate part
@@ -209,6 +210,24 @@ describe("Test Alpha Precompile", () => {
209210
assert.ok(typeof alphaIssuance === 'bigint', "Alpha issuance should be a bigint");
210211
assert.ok(alphaIssuance >= BigInt(0), "Alpha issuance should be non-negative");
211212
});
213+
214+
it("getCKBurn returns valid CK burn rate", async () => {
215+
const ckBurn = await publicClient.readContract({
216+
abi: IAlphaABI,
217+
address: toViemAddress(IALPHA_ADDRESS),
218+
functionName: "getCKBurn",
219+
args: []
220+
})
221+
222+
const ckBurnOnChain = await api.query.SubtensorModule.CKBurn.getValue()
223+
224+
assert.strictEqual(ckBurn, ckBurnOnChain, "CK burn should match on chain");
225+
assert.ok(ckBurn !== undefined, "CK burn should be defined");
226+
const ckBurnPercentage = BigInt(ckBurn) * BigInt(100) / BigInt(2 ** 64 - 1)
227+
assert.ok(ckBurnPercentage >= BigInt(0), "CK burn percentage should be non-negative");
228+
assert.ok(ckBurnPercentage <= BigInt(100), "CK burn percentage should be less than or equal to 100");
229+
assert.ok(typeof ckBurn === 'bigint', "CK burn should be a bigint");
230+
});
212231
});
213232

214233
describe("Global Functions", () => {

pallets/admin-utils/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,20 @@ pub mod pallet {
16091609
pallet_subtensor::Pallet::<T>::set_owner_immune_neuron_limit(netuid, immune_neurons)?;
16101610
Ok(())
16111611
}
1612+
1613+
/// Sets the childkey burn for a subnet.
1614+
/// It is only callable by the root account.
1615+
/// The extrinsic will call the Subtensor pallet to set the childkey burn.
1616+
#[pallet::call_index(73)]
1617+
#[pallet::weight(Weight::from_parts(15_650_000, 0)
1618+
.saturating_add(<T as frame_system::Config>::DbWeight::get().reads(1_u64))
1619+
.saturating_add(<T as frame_system::Config>::DbWeight::get().writes(1_u64)))]
1620+
pub fn sudo_set_ck_burn(origin: OriginFor<T>, burn: u64) -> DispatchResult {
1621+
ensure_root(origin)?;
1622+
pallet_subtensor::Pallet::<T>::set_ck_burn(burn);
1623+
log::debug!("CKBurnSet( burn: {burn:?} ) ");
1624+
Ok(())
1625+
}
16121626
}
16131627
}
16141628

pallets/subtensor/src/coinbase/run_coinbase.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -744,18 +744,19 @@ impl<T: Config> Pallet<T> {
744744
// Calculate the hotkey's share of the validator emission based on its childkey take
745745
let validating_emission: U96F32 = U96F32::saturating_from_num(dividends);
746746
let mut remaining_emission: U96F32 = validating_emission;
747-
let childkey_take_proportion: U96F32 =
747+
let burn_take_proportion: U96F32 = Self::get_ck_burn();
748+
let child_take_proportion: U96F32 =
748749
U96F32::saturating_from_num(Self::get_childkey_take(hotkey, netuid))
749750
.safe_div(U96F32::saturating_from_num(u16::MAX));
750-
log::debug!("Childkey take proportion: {childkey_take_proportion:?} for hotkey {hotkey:?}");
751+
log::debug!("Childkey take proportion: {child_take_proportion:?} for hotkey {hotkey:?}");
751752
// NOTE: Only the validation emission should be split amongst parents.
752753

753754
// Grab the owner of the childkey.
754755
let childkey_owner = Self::get_owning_coldkey_for_hotkey(hotkey);
755756

756757
// Initialize variables to track emission distribution
757758
let mut to_parents: u64 = 0;
758-
let mut total_child_emission_take: U96F32 = U96F32::saturating_from_num(0);
759+
let mut total_child_take: U96F32 = U96F32::saturating_from_num(0);
759760

760761
// Initialize variables to calculate total stakes from parents
761762
let mut total_contribution: U96F32 = U96F32::saturating_from_num(0);
@@ -819,23 +820,26 @@ impl<T: Config> Pallet<T> {
819820
remaining_emission = remaining_emission.saturating_sub(parent_emission);
820821

821822
// Get the childkey take for this parent.
822-
let child_emission_take: U96F32 = if parent_owner == childkey_owner {
823-
// The parent is from the same coldkey, so we don't remove any childkey take.
824-
U96F32::saturating_from_num(0)
825-
} else {
826-
childkey_take_proportion
827-
.saturating_mul(U96F32::saturating_from_num(parent_emission))
823+
let mut burn_take: U96F32 = U96F32::saturating_from_num(0);
824+
let mut child_take: U96F32 = U96F32::saturating_from_num(0);
825+
if parent_owner != childkey_owner {
826+
// The parent is from a different coldkey, we burn some proportion
827+
burn_take = burn_take_proportion.saturating_mul(parent_emission);
828+
child_take = child_take_proportion.saturating_mul(parent_emission);
829+
parent_emission = parent_emission.saturating_sub(burn_take);
830+
parent_emission = parent_emission.saturating_sub(child_take);
831+
total_child_take = total_child_take.saturating_add(child_take);
832+
833+
Self::burn_subnet_alpha(
834+
netuid,
835+
AlphaCurrency::from(burn_take.saturating_to_num::<u64>()),
836+
);
828837
};
838+
log::debug!("burn_takee: {burn_take:?} for hotkey {hotkey:?}");
839+
log::debug!("child_take: {child_take:?} for hotkey {hotkey:?}");
840+
log::debug!("parent_emission: {parent_emission:?} for hotkey {hotkey:?}");
841+
log::debug!("total_child_take: {total_child_take:?} for hotkey {hotkey:?}");
829842

830-
// Remove the childkey take from the parent's emission.
831-
parent_emission = parent_emission.saturating_sub(child_emission_take);
832-
833-
// Add the childkey take to the total childkey take tracker.
834-
total_child_emission_take =
835-
total_child_emission_take.saturating_add(child_emission_take);
836-
837-
log::debug!("Child emission take: {child_emission_take:?} for hotkey {hotkey:?}");
838-
log::debug!("Parent emission: {parent_emission:?} for hotkey {hotkey:?}");
839843
log::debug!("remaining emission: {remaining_emission:?}");
840844

841845
// Add the parent's emission to the distribution list
@@ -853,7 +857,7 @@ impl<T: Config> Pallet<T> {
853857
// Calculate the final emission for the hotkey itself.
854858
// This includes the take left from the parents and the self contribution.
855859
let child_emission = remaining_emission
856-
.saturating_add(total_child_emission_take)
860+
.saturating_add(total_child_take)
857861
.saturating_to_num::<u64>()
858862
.into();
859863

pallets/subtensor/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,12 @@ pub mod pallet {
867867
50400
868868
}
869869

870+
#[pallet::type_value]
871+
/// Default value for ck burn, 18%.
872+
pub fn DefaultCKBurn<T: Config>() -> u64 {
873+
u64::MAX / 100 * 18
874+
}
875+
870876
#[pallet::storage]
871877
pub type MinActivityCutoff<T: Config> =
872878
StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff<T>>;
@@ -920,6 +926,9 @@ pub mod pallet {
920926
/// --- ITEM --> Global weight
921927
pub type TaoWeight<T> = StorageValue<_, u64, ValueQuery, DefaultTaoWeight<T>>;
922928
#[pallet::storage]
929+
/// --- ITEM --> CK burn
930+
pub type CKBurn<T> = StorageValue<_, u64, ValueQuery, DefaultCKBurn<T>>;
931+
#[pallet::storage]
923932
/// --- ITEM ( default_delegate_take )
924933
pub type MaxDelegateTake<T> = StorageValue<_, u16, ValueQuery, DefaultDelegateTake<T>>;
925934
#[pallet::storage]

pallets/subtensor/src/staking/helpers.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,10 @@ impl<T: Config> Pallet<T> {
321321
pub fn is_user_liquidity_enabled(netuid: NetUid) -> bool {
322322
T::SwapInterface::is_user_liquidity_enabled(netuid)
323323
}
324+
325+
pub fn burn_subnet_alpha(netuid: NetUid, amount: AlphaCurrency) {
326+
SubnetAlphaOut::<T>::mutate(netuid, |total| {
327+
*total = total.saturating_sub(amount);
328+
});
329+
}
324330
}

pallets/subtensor/src/staking/stake_utils.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ impl<T: Config> Pallet<T> {
9696
// This ensures the result is always between 0 and 1
9797
weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX))
9898
}
99+
pub fn get_ck_burn() -> U96F32 {
100+
let stored_weight = CKBurn::<T>::get();
101+
let weight_fixed = U96F32::saturating_from_num(stored_weight);
102+
weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX))
103+
}
99104

100105
/// Sets the global global weight in storage.
101106
///
@@ -117,6 +122,11 @@ impl<T: Config> Pallet<T> {
117122
// Update the TaoWeight storage with the new weight value
118123
TaoWeight::<T>::set(weight);
119124
}
125+
// Set the amount burned on non owned CK
126+
pub fn set_ck_burn(weight: u64) {
127+
// Update the ck burn value.
128+
CKBurn::<T>::set(weight);
129+
}
120130

121131
/// Calculates the weighted combination of alpha and global tao for a single hotkey onet a subnet.
122132
///

pallets/subtensor/src/tests/children.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2861,6 +2861,7 @@ fn test_childkey_take_drain() {
28612861

28622862
// Add network, register hotkeys, and setup network parameters
28632863
add_network(netuid, subnet_tempo, 0);
2864+
SubtensorModule::set_ck_burn(0);
28642865
mock::setup_reserves(netuid, (stake * 10_000).into(), (stake * 10_000).into());
28652866
register_ok_neuron(netuid, child_hotkey, child_coldkey, 0);
28662867
register_ok_neuron(netuid, parent_hotkey, parent_coldkey, 1);
@@ -2980,6 +2981,7 @@ fn test_parent_child_chain_emission() {
29802981
let subnet_owner_coldkey = U256::from(1001);
29812982
let subnet_owner_hotkey = U256::from(1002);
29822983
let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
2984+
SubtensorModule::set_ck_burn(0);
29832985
Tempo::<Test>::insert(netuid, 1);
29842986

29852987
// Setup large LPs to prevent slippage
@@ -3192,6 +3194,7 @@ fn test_parent_child_chain_epoch() {
31923194
new_test_ext(1).execute_with(|| {
31933195
let netuid = NetUid::from(1);
31943196
add_network(netuid, 1, 0);
3197+
SubtensorModule::set_ck_burn(0);
31953198
// Set owner cut to 0
31963199
SubtensorModule::set_subnet_owner_cut(0_u16);
31973200

@@ -3336,6 +3339,7 @@ fn test_dividend_distribution_with_children() {
33363339
new_test_ext(1).execute_with(|| {
33373340
let netuid = NetUid::from(1);
33383341
add_network(netuid, 1, 0);
3342+
SubtensorModule::set_ck_burn(0);
33393343
mock::setup_reserves(
33403344
netuid,
33413345
1_000_000_000_000_000.into(),
@@ -3570,6 +3574,7 @@ fn test_dividend_distribution_with_children() {
35703574
fn test_dynamic_parent_child_relationships() {
35713575
new_test_ext(1).execute_with(|| {
35723576
let netuid = NetUid::from(1);
3577+
SubtensorModule::set_ck_burn(0);
35733578
add_network_disable_commit_reveal(netuid, 1, 0);
35743579

35753580
// Define hotkeys and coldkeys

pallets/subtensor/src/tests/coinbase.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,7 @@ fn test_drain_alpha_childkey_parentkey() {
10631063
new_test_ext(1).execute_with(|| {
10641064
let netuid = NetUid::from(1);
10651065
add_network(netuid, 1, 0);
1066+
SubtensorModule::set_ck_burn(0);
10661067
let parent = U256::from(1);
10671068
let child = U256::from(2);
10681069
let coldkey = U256::from(3);
@@ -1238,6 +1239,7 @@ fn test_get_root_children_drain() {
12381239
let alpha = NetUid::from(1);
12391240
add_network(NetUid::ROOT, 1, 0);
12401241
add_network(alpha, 1, 0);
1242+
SubtensorModule::set_ck_burn(0);
12411243
// Set TAO weight to 1.
12421244
SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.
12431245
// Create keys.
@@ -1399,6 +1401,7 @@ fn test_get_root_children_drain_half_proportion() {
13991401
let alpha = NetUid::from(1);
14001402
add_network(NetUid::ROOT, 1, 0);
14011403
add_network(alpha, 1, 0);
1404+
SubtensorModule::set_ck_burn(0);
14021405
// Set TAO weight to 1.
14031406
SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.
14041407
// Create keys.
@@ -1576,6 +1579,7 @@ fn test_get_root_children_drain_with_half_take() {
15761579
add_network(alpha, 1, 0);
15771580
// Set TAO weight to 1.
15781581
SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.
1582+
SubtensorModule::set_ck_burn(0);
15791583
// Create keys.
15801584
let cold_alice = U256::from(0);
15811585
let cold_bob = U256::from(1);
@@ -2750,6 +2754,75 @@ fn test_coinbase_v3_liquidity_update() {
27502754
});
27512755
}
27522756

2757+
// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_alpha_childkey_parentkey_with_burn --exact --show-output --nocapture
2758+
#[test]
2759+
fn test_drain_alpha_childkey_parentkey_with_burn() {
2760+
new_test_ext(1).execute_with(|| {
2761+
let netuid = NetUid::from(1);
2762+
add_network(netuid, 1, 0);
2763+
let parent = U256::from(1);
2764+
let child = U256::from(2);
2765+
let coldkey = U256::from(3);
2766+
let stake_before = AlphaCurrency::from(1_000_000_000);
2767+
register_ok_neuron(netuid, child, coldkey, 0);
2768+
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
2769+
&parent,
2770+
&coldkey,
2771+
netuid,
2772+
stake_before,
2773+
);
2774+
mock_set_children_no_epochs(netuid, &parent, &[(u64::MAX, child)]);
2775+
2776+
// Childkey take is 10%
2777+
ChildkeyTake::<Test>::insert(child, netuid, u16::MAX / 10);
2778+
2779+
let burn_rate = SubtensorModule::get_ck_burn();
2780+
let parent_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid);
2781+
let child_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid);
2782+
2783+
let pending_alpha = AlphaCurrency::from(1_000_000_000);
2784+
SubtensorModule::drain_pending_emission(
2785+
netuid,
2786+
pending_alpha,
2787+
TaoCurrency::ZERO,
2788+
AlphaCurrency::ZERO,
2789+
AlphaCurrency::ZERO,
2790+
);
2791+
let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid);
2792+
let child_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid);
2793+
2794+
let expected_ck_burn = I96F32::from_num(pending_alpha)
2795+
* I96F32::from_num(9.0 / 10.0)
2796+
* I96F32::from_num(burn_rate);
2797+
2798+
let expected_total = I96F32::from_num(pending_alpha) - expected_ck_burn;
2799+
let parent_ratio = (I96F32::from_num(pending_alpha) * I96F32::from_num(9.0 / 10.0)
2800+
- expected_ck_burn)
2801+
/ expected_total;
2802+
let child_ratio = (I96F32::from_num(pending_alpha) / I96F32::from_num(10)) / expected_total;
2803+
2804+
let expected =
2805+
I96F32::from_num(stake_before) + I96F32::from_num(pending_alpha) * parent_ratio;
2806+
log::info!(
2807+
"expected: {:?}, parent_stake_after: {:?}",
2808+
expected.to_num::<u64>(),
2809+
parent_stake_after
2810+
);
2811+
2812+
close(
2813+
expected.to_num::<u64>(),
2814+
parent_stake_after.into(),
2815+
3_000_000,
2816+
);
2817+
let expected = I96F32::from_num(u64::from(pending_alpha)) * child_ratio;
2818+
close(
2819+
expected.to_num::<u64>(),
2820+
child_stake_after.into(),
2821+
3_000_000,
2822+
);
2823+
});
2824+
}
2825+
27532826
#[test]
27542827
fn test_incentive_is_autostaked_to_owner_destination() {
27552828
new_test_ext(1).execute_with(|| {

precompiles/src/alpha.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ where
9090
Ok(U256::from(tao_weight))
9191
}
9292

93+
#[precompile::public("getCKBurn()")]
94+
#[precompile::view]
95+
fn get_ck_burn(_handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
96+
let ck_burn = pallet_subtensor::CKBurn::<R>::get();
97+
Ok(U256::from(ck_burn))
98+
}
99+
93100
#[precompile::public("simSwapTaoForAlpha(uint16,uint64)")]
94101
#[precompile::view]
95102
fn sim_swap_tao_for_alpha(

0 commit comments

Comments
 (0)