-
Notifications
You must be signed in to change notification settings - Fork 319
Add lock-aware stake transfer extrinsic #2717
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
base: devnet-ready
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,7 @@ impl<T: Config> Pallet<T> { | |
| None, | ||
| None, | ||
| false, | ||
| None, | ||
| )?; | ||
|
|
||
| // Log the event. | ||
|
|
@@ -140,6 +141,7 @@ impl<T: Config> Pallet<T> { | |
| None, | ||
| None, | ||
| true, | ||
| None, | ||
| )?; | ||
|
|
||
| // 9. Emit an event for logging/monitoring. | ||
|
|
@@ -159,6 +161,51 @@ impl<T: Config> Pallet<T> { | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Transfers either locked or unlocked stake from one coldkey to another within one subnet. | ||
| /// | ||
| /// This follows `do_transfer_stake`, but caps the requested amount to the | ||
| /// selected lock bucket. If `locked` is true, only locked alpha can move and | ||
| /// the matching lock state follows the stake. If `locked` is false, only | ||
| /// unlocked alpha can move. | ||
| pub fn do_transfer_stake_lock_aware( | ||
| origin: OriginFor<T>, | ||
| destination_coldkey: T::AccountId, | ||
| hotkey: T::AccountId, | ||
| netuid: NetUid, | ||
| alpha_amount: AlphaBalance, | ||
| locked: bool, | ||
| ) -> dispatch::DispatchResult { | ||
| let coldkey = ensure_signed(origin)?; | ||
|
|
||
| let tao_moved = Self::transition_stake_internal( | ||
| &coldkey, | ||
| &destination_coldkey, | ||
| &hotkey, | ||
| &hotkey, | ||
| netuid, | ||
| netuid, | ||
| alpha_amount, | ||
| None, | ||
| None, | ||
| true, | ||
| Some(locked), | ||
| )?; | ||
|
|
||
| log::debug!( | ||
| "StakeTransferred(origin_coldkey: {coldkey:?}, destination_coldkey: {destination_coldkey:?}, hotkey: {hotkey:?}, netuid: {netuid:?}, amount: {tao_moved:?})" | ||
| ); | ||
| Self::deposit_event(Event::StakeTransferred( | ||
| coldkey, | ||
| destination_coldkey, | ||
| hotkey, | ||
| netuid, | ||
| netuid, | ||
| tao_moved, | ||
| )); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Swaps a specified amount of stake for the same `(coldkey, hotkey)` pair from one subnet | ||
| /// (`origin_netuid`) to another (`destination_netuid`). | ||
| /// | ||
|
|
@@ -204,6 +251,7 @@ impl<T: Config> Pallet<T> { | |
| None, | ||
| None, | ||
| false, | ||
| None, | ||
| )?; | ||
|
|
||
| // Emit an event for logging. | ||
|
|
@@ -271,6 +319,7 @@ impl<T: Config> Pallet<T> { | |
| Some(limit_price), | ||
| Some(allow_partial), | ||
| false, | ||
| None, | ||
| )?; | ||
|
|
||
| // Emit an event for logging. | ||
|
|
@@ -302,6 +351,7 @@ impl<T: Config> Pallet<T> { | |
| maybe_limit_price: Option<TaoBalance>, | ||
| maybe_allow_partial: Option<bool>, | ||
| check_transfer_toggle: bool, | ||
| lock_aware_transfer: Option<bool>, | ||
| ) -> Result<TaoBalance, DispatchError> { | ||
| // Cap the alpha_amount at available Alpha because user might be paying transaxtion fees | ||
| // in Alpha and their total is already reduced by now. | ||
|
|
@@ -311,6 +361,15 @@ impl<T: Config> Pallet<T> { | |
| origin_netuid, | ||
| ); | ||
| let alpha_amount = alpha_amount.min(alpha_available); | ||
| let alpha_amount = lock_aware_transfer | ||
| .map(|locked| { | ||
| alpha_amount.min(Self::lock_aware_transferable_alpha( | ||
|
Comment on lines
+364
to
+366
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] Lock-aware cap is not bound to the selected hotkey
|
||
| origin_coldkey, | ||
| origin_netuid, | ||
| locked, | ||
| )) | ||
| }) | ||
| .unwrap_or(alpha_amount); | ||
|
Comment on lines
+364
to
+372
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] Capped lock-aware amount can mutate stake before failing minimum check For same-subnet lock-aware transfers, this cap can reduce a caller-supplied |
||
|
|
||
| // Calculate the maximum amount that can be executed | ||
| let max_amount = if origin_netuid != destination_netuid { | ||
|
|
@@ -393,6 +452,7 @@ impl<T: Config> Pallet<T> { | |
| destination_hotkey, | ||
| origin_netuid, | ||
| move_amount, | ||
| lock_aware_transfer, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[HIGH] Cross-subnet locked transfers do not move lock state
Some(locked)is passed into the generic transition path even whenorigin_netuid != destination_netuid, but the cross-subnet branch later usesunstake_from_subnetandstake_into_subnetrather thantransfer_lock. That means no lock state is moved to the destination subnet/coldkey. Worse,validate_stake_transitionstill enforcesensure_available_to_unstakefor cross-subnet moves, so a fully locked position fails withStakeUnavailable, while a partially locked position with enough unlocked alpha can succeed by moving unlocked stake and leaving the source lock behind. This contradicts the new extrinsic docs and the PR body claim that locked transfers preserve/move the lock state. Either rejectorigin_netuid != destination_netuidfor this extrinsic, or implement explicit cross-subnet lock migration and add a test thatlocked=truemoves lock state across netuids.