Skip to content
Open
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
48 changes: 47 additions & 1 deletion chain-extensions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,10 @@ where
}
}
}
// Backward-compatible TransferStakeV1 with 5 parameters (same hotkey for origin and destination)
FunctionId::TransferStakeV1 => {
let weight = Weight::from_parts(160_300_000, 0)
.saturating_add(T::DbWeight::get().reads(13_u64))
.saturating_add(T::DbWeight::get().reads(14_u64))
.saturating_add(T::DbWeight::get().writes(6_u64));

env.charge_weight(weight)?;
Expand All @@ -244,6 +245,7 @@ where
let call_result = pallet_subtensor::Pallet::<T>::transfer_stake(
RawOrigin::Signed(env.caller()).into(),
destination_coldkey,
hotkey.clone(),
hotkey,
origin_netuid,
destination_netuid,
Expand All @@ -258,6 +260,50 @@ where
}
}
}
// New TransferStakeV2 with 6 parameters (allows different origin and destination hotkeys)
FunctionId::TransferStakeV2 => {
let weight = Weight::from_parts(160_300_000, 0)
.saturating_add(T::DbWeight::get().reads(14_u64))
.saturating_add(T::DbWeight::get().writes(6_u64));

env.charge_weight(weight)?;

let (
destination_coldkey,
origin_hotkey,
destination_hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
): (
T::AccountId,
T::AccountId,
T::AccountId,
NetUid,
NetUid,
AlphaCurrency,
) = env
.read_as()
.map_err(|_| DispatchError::Other("Failed to decode input parameters"))?;

let call_result = pallet_subtensor::Pallet::<T>::transfer_stake(
RawOrigin::Signed(env.caller()).into(),
destination_coldkey,
origin_hotkey,
destination_hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
);

match call_result {
Ok(_) => Ok(RetVal::Converging(Output::Success as u32)),
Err(e) => {
let error_code = Output::from(e) as u32;
Ok(RetVal::Converging(error_code))
}
}
}
FunctionId::SwapStakeV1 => {
let weight = Weight::from_parts(351_300_000, 0)
.saturating_add(T::DbWeight::get().reads(35_u64))
Expand Down
2 changes: 1 addition & 1 deletion chain-extensions/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ fn transfer_stake_success_moves_between_coldkeys() {
let alpha_to_transfer: AlphaCurrency = (alpha_before.to_u64() / 3).into();

let expected_weight = Weight::from_parts(160_300_000, 0)
.saturating_add(<mock::Test as frame_system::Config>::DbWeight::get().reads(13))
.saturating_add(<mock::Test as frame_system::Config>::DbWeight::get().reads(14))
.saturating_add(<mock::Test as frame_system::Config>::DbWeight::get().writes(6));

let mut env = MockEnv::new(
Expand Down
1 change: 1 addition & 0 deletions chain-extensions/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum FunctionId {
AddProxyV1 = 13,
RemoveProxyV1 = 14,
GetAlphaPriceV1 = 15,
TransferStakeV2 = 16,
}

#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug)]
Expand Down
38 changes: 38 additions & 0 deletions contract-tests/bittensor/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum FunctionId {
AddProxyV1 = 13,
RemoveProxyV1 = 14,
GetAlphaPriceV1 = 15,
TransferStakeV2 = 16,
}

#[ink::chain_extension(extension = 0x1000)]
Expand Down Expand Up @@ -65,6 +66,7 @@ pub trait RuntimeReadWrite {
amount: AlphaCurrency,
);

// Backward-compatible transfer_stake with 5 parameters (same hotkey)
#[ink(function = 6)]
fn transfer_stake(
destination_coldkey: <CustomEnvironment as ink::env::Environment>::AccountId,
Expand All @@ -74,6 +76,17 @@ pub trait RuntimeReadWrite {
amount: AlphaCurrency,
);

// New transfer_stake_v2 with 6 parameters (different hotkeys)
#[ink(function = 16)]
fn transfer_stake_v2(
destination_coldkey: <CustomEnvironment as ink::env::Environment>::AccountId,
origin_hotkey: <CustomEnvironment as ink::env::Environment>::AccountId,
destination_hotkey: <CustomEnvironment as ink::env::Environment>::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
amount: AlphaCurrency,
);

#[ink(function = 7)]
fn swap_stake(
hotkey: <CustomEnvironment as ink::env::Environment>::AccountId,
Expand Down Expand Up @@ -274,6 +287,7 @@ mod bittensor {
.map_err(|_e| ReadWriteErrorCode::WriteFailed)
}

// Backward-compatible transfer_stake with 5 parameters (same hotkey)
#[ink(message)]
pub fn transfer_stake(
&self,
Expand All @@ -295,6 +309,30 @@ mod bittensor {
.map_err(|_e| ReadWriteErrorCode::WriteFailed)
}

// New transfer_stake_v2 with 6 parameters (different hotkeys)
#[ink(message)]
pub fn transfer_stake_v2(
&self,
destination_coldkey: [u8; 32],
origin_hotkey: [u8; 32],
destination_hotkey: [u8; 32],
origin_netuid: u16,
destination_netuid: u16,
amount: u64,
) -> Result<(), ReadWriteErrorCode> {
self.env()
.extension()
.transfer_stake_v2(
destination_coldkey.into(),
origin_hotkey.into(),
destination_hotkey.into(),
origin_netuid.into(),
destination_netuid.into(),
amount.into(),
)
.map_err(|_e| ReadWriteErrorCode::WriteFailed)
}

#[ink(message)]
pub fn swap_stake(
&self,
Expand Down
3 changes: 2 additions & 1 deletion docs/wasm-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ Subtensor provides a custom chain extension that allows smart contracts to inter
| 3 | `unstake_all` | Unstake all TAO from a hotkey | `(AccountId)` | Error code |
| 4 | `unstake_all_alpha` | Unstake all Alpha from a hotkey | `(AccountId)` | Error code |
| 5 | `move_stake` | Move stake between hotkeys | `(AccountId, AccountId, NetUid, NetUid, AlphaCurrency)` | Error code |
| 6 | `transfer_stake` | Transfer stake between coldkeys | `(AccountId, AccountId, NetUid, NetUid, AlphaCurrency)` | Error code |
| 6 | `transfer_stake` | Transfer stake between coldkeys (same hotkey) | `(AccountId, AccountId, NetUid, NetUid, AlphaCurrency)` | Error code |
| 16 | `transfer_stake_v2` | Transfer stake between coldkeys and hotkeys | `(AccountId, AccountId, AccountId, NetUid, NetUid, AlphaCurrency)` | Error code |
| 7 | `swap_stake` | Swap stake allocations between subnets | `(AccountId, NetUid, NetUid, AlphaCurrency)` | Error code |
| 8 | `add_stake_limit` | Delegate stake with a price limit | `(AccountId, NetUid, TaoCurrency, TaoCurrency, bool)` | Error code |
| 9 | `remove_stake_limit` | Withdraw stake with a price limit | `(AccountId, NetUid, AlphaCurrency, TaoCurrency, bool)` | Error code |
Expand Down
19 changes: 11 additions & 8 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1636,13 +1636,14 @@ mod dispatches {
)
}

/// Transfers a specified amount of stake from one coldkey to another, optionally across subnets,
/// while keeping the same hotkey.
/// Transfers a specified amount of stake from one coldkey to another, optionally across subnets
/// and hotkeys.
///
/// # Arguments
/// * `origin` - The origin of the transaction, which must be signed by the `origin_coldkey`.
/// * `destination_coldkey` - The coldkey to which the stake is transferred.
/// * `hotkey` - The hotkey associated with the stake.
/// * `origin_hotkey` - The hotkey from which the stake is being transferred.
/// * `destination_hotkey` - The hotkey to which the stake is being transferred.
/// * `origin_netuid` - The network/subnet ID to move stake from.
/// * `destination_netuid` - The network/subnet ID to move stake to (for cross-subnet transfer).
/// * `alpha_amount` - The amount of stake to transfer.
Expand All @@ -1651,28 +1652,30 @@ mod dispatches {
/// Returns an error if:
/// * The origin is not signed by the correct coldkey.
/// * Either subnet does not exist.
/// * The hotkey does not exist.
/// * There is insufficient stake on `(origin_coldkey, hotkey, origin_netuid)`.
/// * Either hotkey does not exist.
/// * There is insufficient stake on `(origin_coldkey, origin_hotkey, origin_netuid)`.
/// * The transfer amount is below the minimum stake requirement.
///
/// # Events
/// May emit a `StakeTransferred` event on success.
#[pallet::call_index(86)]
#[pallet::weight((Weight::from_parts(160_300_000, 0)
.saturating_add(T::DbWeight::get().reads(13_u64))
.saturating_add(T::DbWeight::get().reads(14_u64))
.saturating_add(T::DbWeight::get().writes(6_u64)), DispatchClass::Normal, Pays::Yes))]
pub fn transfer_stake(
origin: T::RuntimeOrigin,
destination_coldkey: T::AccountId,
hotkey: T::AccountId,
origin_hotkey: T::AccountId,
destination_hotkey: T::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
alpha_amount: AlphaCurrency,
) -> DispatchResult {
Self::do_transfer_stake(
origin,
destination_coldkey,
hotkey,
origin_hotkey,
destination_hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
Expand Down
5 changes: 3 additions & 2 deletions pallets/subtensor/src/macros/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,14 @@ mod events {
/// - **error**: The dispatch error emitted by the failed item.
BatchWeightItemFailed(sp_runtime::DispatchError),

/// Stake has been transferred from one coldkey to another on the same subnet.
/// Stake has been transferred from one coldkey to another, optionally across hotkeys and subnets.
/// Parameters:
/// (origin_coldkey, destination_coldkey, hotkey, origin_netuid, destination_netuid, amount)
/// (origin_coldkey, destination_coldkey, origin_hotkey, destination_hotkey, origin_netuid, destination_netuid, amount)
StakeTransferred(
T::AccountId,
T::AccountId,
T::AccountId,
T::AccountId,
NetUid,
NetUid,
TaoCurrency,
Expand Down
27 changes: 15 additions & 12 deletions pallets/subtensor/src/staking/move_stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,14 @@ impl<T: Config> Pallet<T> {
Ok(())
}

/// Transfers stake from one coldkey to another, optionally moving from one subnet to another,
/// while keeping the same hotkey.
/// Transfers stake from one coldkey to another, optionally moving from one subnet to another
/// and from one hotkey to another.
///
/// # Arguments
/// * `origin` - The origin of the transaction, which must be signed by the `origin_coldkey`.
/// * `destination_coldkey` - The account ID of the coldkey to which the stake is being transferred.
/// * `hotkey` - The account ID of the hotkey associated with this stake.
/// * `origin_hotkey` - The account ID of the hotkey from which the stake is being transferred.
/// * `destination_hotkey` - The account ID of the hotkey to which the stake is being transferred.
/// * `origin_netuid` - The network ID (subnet) from which the stake is being transferred.
/// * `destination_netuid` - The network ID (subnet) to which the stake is being transferred.
/// * `alpha_amount` - The amount of stake to transfer.
Expand All @@ -111,8 +112,8 @@ impl<T: Config> Pallet<T> {
/// This function will return an error if:
/// * The transaction is not signed by the `origin_coldkey`.
/// * The subnet (`origin_netuid` or `destination_netuid`) does not exist.
/// * The `hotkey` does not exist.
/// * The `(origin_coldkey, hotkey, origin_netuid)` does not have enough stake for `alpha_amount`.
/// * Either `origin_hotkey` or `destination_hotkey` does not exist.
/// * The `(origin_coldkey, origin_hotkey, origin_netuid)` does not have enough stake for `alpha_amount`.
/// * The amount to be transferred is below the minimum stake requirement.
/// * There is a failure in staking or unstaking logic.
///
Expand All @@ -121,7 +122,8 @@ impl<T: Config> Pallet<T> {
pub fn do_transfer_stake(
origin: T::RuntimeOrigin,
destination_coldkey: T::AccountId,
hotkey: T::AccountId,
origin_hotkey: T::AccountId,
destination_hotkey: T::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
alpha_amount: AlphaCurrency,
Expand All @@ -133,8 +135,8 @@ impl<T: Config> Pallet<T> {
let tao_moved = Self::transition_stake_internal(
&coldkey,
&destination_coldkey,
&hotkey,
&hotkey,
&origin_hotkey,
&destination_hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
Expand All @@ -144,20 +146,21 @@ impl<T: Config> Pallet<T> {
false,
)?;

// 9. Emit an event for logging/monitoring.
// Emit an event for logging/monitoring.
log::debug!(
"StakeTransferred(origin_coldkey: {coldkey:?}, destination_coldkey: {destination_coldkey:?}, hotkey: {hotkey:?}, origin_netuid: {origin_netuid:?}, destination_netuid: {destination_netuid:?}, amount: {tao_moved:?})"
"StakeTransferred(origin_coldkey: {coldkey:?}, destination_coldkey: {destination_coldkey:?}, origin_hotkey: {origin_hotkey:?}, destination_hotkey: {destination_hotkey:?}, origin_netuid: {origin_netuid:?}, destination_netuid: {destination_netuid:?}, amount: {tao_moved:?})"
);
Self::deposit_event(Event::StakeTransferred(
coldkey,
destination_coldkey,
hotkey,
origin_hotkey,
destination_hotkey,
origin_netuid,
destination_netuid,
tao_moved,
));

// 10. Return success.
// Return success.
Ok(())
}

Expand Down
Loading