Skip to content

Feat/subtoken enable #1512

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

Merged
merged 22 commits into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions evm-tests/src/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ export async function estimateTransactionCost(provider: Provider, tx: Transactio
export function getContract(contractAddress: string, abi: {}[], wallet: Wallet) {
const contract = new ethers.Contract(contractAddress, abi, wallet);
return contract

}
}
52 changes: 44 additions & 8 deletions evm-tests/src/subtensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { KeyPair } from "@polkadot-labs/hdkd-helpers"
import { getAliceSigner, waitForTransactionCompletion, getSignerFromKeypair } from './substrate'
import { convertH160ToSS58, convertPublicKeyToSs58 } from './address-utils'
import { tao } from './balance-math'
import internal from "stream";

// create a new subnet and return netuid
export async function addNewSubnetwork(api: TypedApi<typeof devnet>, hotkey: KeyPair, coldkey: KeyPair) {
Expand Down Expand Up @@ -293,52 +294,87 @@ export async function setMinDelegateTake(api: TypedApi<typeof devnet>, minDelega
}

export async function becomeDelegate(api: TypedApi<typeof devnet>, ss58Address: string, keypair: KeyPair) {
const singer = getSignerFromKeypair(keypair)
const signer = getSignerFromKeypair(keypair)

const tx = api.tx.SubtensorModule.become_delegate({
hotkey: ss58Address
})
await waitForTransactionCompletion(api, tx, singer)
await waitForTransactionCompletion(api, tx, signer)
.then(() => { })
.catch((error) => { console.log(`transaction error ${error}`) });
}

export async function addStake(api: TypedApi<typeof devnet>, netuid: number, ss58Address: string, amount_staked: bigint, keypair: KeyPair) {
const singer = getSignerFromKeypair(keypair)
const signer = getSignerFromKeypair(keypair)
let tx = api.tx.SubtensorModule.add_stake({
netuid: netuid,
hotkey: ss58Address,
amount_staked: amount_staked
})

await waitForTransactionCompletion(api, tx, singer)
await waitForTransactionCompletion(api, tx, signer)
.then(() => { })
.catch((error) => { console.log(`transaction error ${error}`) });

}

export async function setWeight(api: TypedApi<typeof devnet>, netuid: number, dests: number[], weights: number[], version_key: bigint, keypair: KeyPair) {
const singer = getSignerFromKeypair(keypair)
const signer = getSignerFromKeypair(keypair)
let tx = api.tx.SubtensorModule.set_weights({
netuid: netuid,
dests: dests,
weights: weights,
version_key: version_key
})

await waitForTransactionCompletion(api, tx, singer)
await waitForTransactionCompletion(api, tx, signer)
.then(() => { })
.catch((error) => { console.log(`transaction error ${error}`) });

}

export async function rootRegister(api: TypedApi<typeof devnet>, ss58Address: string, keypair: KeyPair) {
const singer = getSignerFromKeypair(keypair)
const signer = getSignerFromKeypair(keypair)
let tx = api.tx.SubtensorModule.root_register({
hotkey: ss58Address
})

await waitForTransactionCompletion(api, tx, singer)
await waitForTransactionCompletion(api, tx, signer)
.then(() => { })
.catch((error) => { console.log(`transaction error ${error}`) });
}

export async function setSubtokenEnable(api: TypedApi<typeof devnet>, netuid: number, subtokenEnable: boolean) {
const signer = getAliceSigner()
let internalTx = api.tx.AdminUtils.sudo_set_subtoken_enabled({
netuid: netuid,
subtoken_enabled: subtokenEnable
})
let tx = api.tx.Sudo.sudo({ call: internalTx.decodedCall })

await waitForTransactionCompletion(api, tx, signer)
.then(() => { })
.catch((error) => { console.log(`transaction error ${error}`) });

}

export async function startCall(api: TypedApi<typeof devnet>, netuid: number, keypair: KeyPair) {
const registerBlock = Number(await api.query.SubtensorModule.NetworkRegisteredAt.getValue(netuid))
let currentBlock = await api.query.System.Number.getValue()
const duration = Number(await api.constants.SubtensorModule.DurationOfStartCall)

while (currentBlock - registerBlock <= duration) {
await new Promise((resolve) => setTimeout(resolve, 2000));
currentBlock = await api.query.System.Number.getValue()
}
await new Promise((resolve) => setTimeout(resolve, 2000));

const signer = getSignerFromKeypair(keypair)
let tx = api.tx.SubtensorModule.start_call({
netuid: netuid,
})

await waitForTransactionCompletion(api, tx, signer)
.then(() => { })
.catch((error) => { console.log(`transaction error ${error}`) });

Expand Down
4 changes: 3 additions & 1 deletion evm-tests/test/neuron.precompile.emission-check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { convertPublicKeyToSs58, } from "../src/address-utils"
import { ethers } from "ethers"
import { INEURON_ADDRESS, INeuronABI } from "../src/contracts/neuron"
import { generateRandomEthersWallet } from "../src/utils"
import { forceSetBalanceToSs58Address, forceSetBalanceToEthAddress, addNewSubnetwork } from "../src/subtensor"
import { forceSetBalanceToSs58Address, forceSetBalanceToEthAddress, addNewSubnetwork, startCall, setSubtokenEnable } from "../src/subtensor"

describe("Test the Neuron precompile with emission", () => {
// init eth part
Expand Down Expand Up @@ -39,11 +39,13 @@ describe("Test the Neuron precompile with emission", () => {
await forceSetBalanceToEthAddress(api, wallet.address)

const netuid = await addNewSubnetwork(api, hotkey2, coldkey)
await startCall(api, netuid, coldkey)
console.log("test on subnet ", netuid)
})

it("Burned register and check emission", async () => {
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1

const uid = await api.query.SubtensorModule.SubnetworkN.getValue(netuid)
const contract = new ethers.Contract(INEURON_ADDRESS, INeuronABI, wallet);

Expand Down
4 changes: 3 additions & 1 deletion evm-tests/test/neuron.precompile.reveal-weights.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { convertH160ToPublicKey } from "../src/address-utils"
import { blake2AsU8a } from "@polkadot/util-crypto"
import {
forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, setCommitRevealWeightsEnabled, setWeightsSetRateLimit, burnedRegister,
setTempo, setCommitRevealWeightsInterval
setTempo, setCommitRevealWeightsInterval,
startCall
} from "../src/subtensor"

// hardcode some values for reveal hash
Expand Down Expand Up @@ -64,6 +65,7 @@ describe("Test neuron precompile reveal weights", () => {
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey))
await forceSetBalanceToEthAddress(api, wallet.address)
let netuid = await addNewSubnetwork(api, hotkey, coldkey)
await startCall(api, netuid, coldkey)

console.log("test the case on subnet ", netuid)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { convertPublicKeyToSs58, convertH160ToSS58 } from "../src/address-utils"
import { ethers } from "ethers"
import { INEURON_ADDRESS, INeuronABI } from "../src/contracts/neuron"
import { generateRandomEthersWallet } from "../src/utils"
import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister } from "../src/subtensor"
import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, startCall } from "../src/subtensor"

describe("Test neuron precompile Serve Axon Prometheus", () => {
// init eth part
Expand Down Expand Up @@ -34,6 +34,7 @@ describe("Test neuron precompile Serve Axon Prometheus", () => {
await forceSetBalanceToEthAddress(api, wallet2.address)
await forceSetBalanceToEthAddress(api, wallet3.address)
let netuid = await addNewSubnetwork(api, hotkey, coldkey)
await startCall(api, netuid, coldkey)

console.log("test the case on subnet ", netuid)

Expand Down
4 changes: 3 additions & 1 deletion evm-tests/test/neuron.precompile.set-weights.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { INEURON_ADDRESS, INeuronABI } from "../src/contracts/neuron"
import { generateRandomEthersWallet } from "../src/utils"
import {
forceSetBalanceToSs58Address, forceSetBalanceToEthAddress, addNewSubnetwork, burnedRegister, setCommitRevealWeightsEnabled,
setWeightsSetRateLimit
setWeightsSetRateLimit,
startCall
} from "../src/subtensor"

describe("Test neuron precompile contract, set weights function", () => {
Expand All @@ -31,6 +32,7 @@ describe("Test neuron precompile contract, set weights function", () => {
await forceSetBalanceToEthAddress(api, wallet.address)

const netuid = await addNewSubnetwork(api, hotkey, coldkey)
await startCall(api, netuid, coldkey)
console.log("test on subnet ", netuid)

await burnedRegister(api, netuid, convertH160ToSS58(wallet.address), coldkey)
Expand Down
4 changes: 3 additions & 1 deletion evm-tests/test/staking.precompile.add-remove.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import { convertH160ToPublicKey } from "../src/address-utils"
import {
forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister,
sendProxyCall,
startCall,
} from "../src/subtensor"
import { ETH_LOCAL_URL } from "../src/config";
import { ISTAKING_ADDRESS, ISTAKING_V2_ADDRESS, IStakingABI, IStakingV2ABI } from "../src/contracts/staking"
import { PublicClient } from "viem";

describe("Test neuron precompile reveal weights", () => {
describe("Test neuron precompile add remove stake", () => {
// init eth part
const wallet1 = generateRandomEthersWallet();
const wallet2 = generateRandomEthersWallet();
Expand All @@ -41,6 +42,7 @@ describe("Test neuron precompile reveal weights", () => {
await forceSetBalanceToEthAddress(api, wallet1.address)
await forceSetBalanceToEthAddress(api, wallet2.address)
let netuid = await addNewSubnetwork(api, hotkey, coldkey)
await startCall(api, netuid, coldkey)

console.log("test the case on subnet ", netuid)

Expand Down
6 changes: 4 additions & 2 deletions evm-tests/test/staking.precompile.reward.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { tao } from "../src/balance-math"
import {
forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister,
setTxRateLimit, setTempo, setWeightsSetRateLimit, setSubnetOwnerCut, setMaxAllowedUids,
setMinDelegateTake, becomeDelegate, setActivityCutoff, addStake, setWeight, rootRegister
setMinDelegateTake, becomeDelegate, setActivityCutoff, addStake, setWeight, rootRegister,
startCall
} from "../src/subtensor"

describe("Test neuron precompile reveal weights", () => {
describe("Test neuron precompile reward", () => {
const hotkey = getRandomSubstrateKeypair();
const coldkey = getRandomSubstrateKeypair();

Expand All @@ -35,6 +36,7 @@ describe("Test neuron precompile reveal weights", () => {
// await forceSetBalanceToEthAddress(api, wallet1.address)
// await forceSetBalanceToEthAddress(api, wallet2.address)
let netuid = await addNewSubnetwork(api, hotkey, coldkey)
await startCall(api, netuid, coldkey)

console.log("test the case on subnet ", netuid)

Expand Down
5 changes: 4 additions & 1 deletion evm-tests/test/staking.precompile.stake-get.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { convertPublicKeyToSs58 } from "../src/address-utils"
import { tao } from "../src/balance-math"
import {
forceSetBalanceToSs58Address, addNewSubnetwork, addStake,
startCall
} from "../src/subtensor"
import { ethers } from "ethers";
import { generateRandomEthersWallet } from "../src/utils"
Expand All @@ -23,7 +24,9 @@ describe("Test staking precompile get methods", () => {
api = await getDevnetApi()
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey))
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey))
let netuid = await addNewSubnetwork(api, hotkey, coldkey)
await addNewSubnetwork(api, hotkey, coldkey)
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1
await startCall(api, netuid, coldkey)
console.log("will test in subnet: ", netuid)
})

Expand Down
30 changes: 30 additions & 0 deletions pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,36 @@ pub mod pallet {
);
Ok(())
}

/// Enables or disables subtoken trading for a given subnet.
///
/// # Arguments
/// * `origin` - The origin of the call, which must be the root account.
/// * `netuid` - The unique identifier of the subnet.
/// * `subtoken_enabled` - A boolean indicating whether subtoken trading should be enabled or disabled.
///
/// # Errors
/// * `BadOrigin` - If the caller is not the root account.
///
/// # Weight
/// Weight is handled by the `#[pallet::weight]` attribute.
#[pallet::call_index(66)]
#[pallet::weight((0, DispatchClass::Operational, Pays::No))]
pub fn sudo_set_subtoken_enabled(
origin: OriginFor<T>,
netuid: u16,
subtoken_enabled: bool,
) -> DispatchResult {
ensure_root(origin)?;
pallet_subtensor::SubtokenEnabled::<T>::set(netuid, subtoken_enabled);

log::debug!(
"SubtokenEnabled( netuid: {:?}, subtoken_enabled: {:?} )",
netuid,
subtoken_enabled
);
Ok(())
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,9 @@ pub mod pallet {
/// MAP ( netuid ) --> (alpha_low, alpha_high)
pub type AlphaValues<T> =
StorageMap<_, Identity, u16, (u16, u16), ValueQuery, DefaultAlphaValues<T>>;
#[pallet::storage]
/// --- MAP ( netuid ) --> If subtoken trading enabled
pub type SubtokenEnabled<T> = StorageMap<_, Identity, u16, bool, ValueQuery, DefaultFalse<T>>;

/// =======================================
/// ==== Subnetwork Consensus Storage ====
Expand Down Expand Up @@ -1656,6 +1659,15 @@ pub mod pallet {
}
true
}

/// Ensure subtoken enalbed
pub fn ensure_subtoken_enabled(subnet: u16) -> DispatchResult {
ensure!(
SubtokenEnabled::<T>::get(subnet),
Error::<T>::SubtokenDisabled
);
Ok(())
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions pallets/subtensor/src/macros/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,7 @@ mod errors {
UnableToRecoverPublicKey,
/// Recovered public key is invalid.
InvalidRecoveredPublicKey,
/// SubToken disabled now
SubtokenDisabled,
}
}
5 changes: 4 additions & 1 deletion pallets/subtensor/src/macros/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ mod hooks {
// Remove all zero value entries in TotalHotkeyAlpha
.saturating_add(migrations::migrate_remove_zero_total_hotkey_alpha::migrate_remove_zero_total_hotkey_alpha::<T>())
// Wipe existing items to prevent bad decoding for new type
.saturating_add(migrations::migrate_upgrade_revealed_commitments::migrate_upgrade_revealed_commitments::<T>());
.saturating_add(migrations::migrate_upgrade_revealed_commitments::migrate_upgrade_revealed_commitments::<T>())
// Set subtoken enabled for all existed subnets
.saturating_add(migrations::migrate_set_subtoken_enabled::migrate_set_subtoken_enabled::<T>())
;
weight
}

Expand Down
54 changes: 54 additions & 0 deletions pallets/subtensor/src/migrations/migrate_set_subtoken_enabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use super::*;
use crate::HasMigrationRun;
use frame_support::{traits::Get, weights::Weight};
use scale_info::prelude::string::String;

pub fn migrate_set_subtoken_enabled<T: Config>() -> Weight {
let migration_name = b"migrate_set_subtoken_enabled".to_vec();

let mut weight = T::DbWeight::get().reads(1);
if HasMigrationRun::<T>::get(&migration_name) {
log::info!(
"Migration '{:?}' has already run. Skipping.",
String::from_utf8_lossy(&migration_name)
);
return weight;
}

log::info!(
"Running migration '{:?}'",
String::from_utf8_lossy(&migration_name)
);

// ------------------------------
// Step 1: Set the subnet token enabled for all subnets except new subnet
// ------------------------------
let netuids = Pallet::<T>::get_all_subnet_netuids();
for netuid in netuids.iter() {
if *netuid != 0 {
// set it as true if start call executed and value exists for first emission block number
SubtokenEnabled::<T>::insert(
netuid,
FirstEmissionBlockNumber::<T>::get(netuid).is_some(),
);
} else {
SubtokenEnabled::<T>::insert(netuid, true);
}
}

// ------------------------------
// Step 2: Mark Migration as Completed
// ------------------------------

HasMigrationRun::<T>::insert(&migration_name, true);

weight =
weight.saturating_add(T::DbWeight::get().writes((netuids.len() as u64).saturating_add(1)));

log::info!(
"Migration '{:?}' completed successfully.",
String::from_utf8_lossy(&migration_name)
);

weight
}
1 change: 1 addition & 0 deletions pallets/subtensor/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod migrate_remove_zero_total_hotkey_alpha;
pub mod migrate_set_first_emission_block_number;
pub mod migrate_set_min_burn;
pub mod migrate_set_min_difficulty;
pub mod migrate_set_subtoken_enabled;
pub mod migrate_stake_threshold;
pub mod migrate_subnet_volume;
pub mod migrate_to_v1_separate_emission;
Expand Down
Loading
Loading