bittensor_rs/extrinsics/
transfer.rs1use crate::api::api;
9use crate::error::BittensorError;
10use crate::extrinsics::ExtrinsicResponse;
11use crate::types::Balance;
12use crate::AccountId;
13use std::str::FromStr;
14use subxt::OnlineClient;
15use subxt::PolkadotConfig;
16
17#[derive(Debug, Clone)]
19pub struct TransferParams {
20 pub dest: String,
22 pub amount_rao: u64,
24 pub keep_alive: bool,
26}
27
28impl TransferParams {
29 pub fn new_tao(dest: &str, amount_tao: f64) -> Self {
43 Self {
44 dest: dest.to_string(),
45 amount_rao: (amount_tao * 1_000_000_000.0) as u64,
46 keep_alive: true,
47 }
48 }
49
50 pub fn new_rao(dest: &str, amount_rao: u64) -> Self {
52 Self {
53 dest: dest.to_string(),
54 amount_rao,
55 keep_alive: true,
56 }
57 }
58
59 pub fn keep_alive(mut self, keep_alive: bool) -> Self {
61 self.keep_alive = keep_alive;
62 self
63 }
64}
65
66pub async fn transfer<S>(
93 client: &OnlineClient<PolkadotConfig>,
94 signer: &S,
95 params: TransferParams,
96) -> Result<ExtrinsicResponse<Balance>, BittensorError>
97where
98 S: subxt::tx::Signer<PolkadotConfig>,
99{
100 if params.keep_alive {
101 transfer_keep_alive(client, signer, params).await
102 } else {
103 transfer_allow_death(client, signer, params).await
104 }
105}
106
107pub async fn transfer_keep_alive<S>(
111 client: &OnlineClient<PolkadotConfig>,
112 signer: &S,
113 params: TransferParams,
114) -> Result<ExtrinsicResponse<Balance>, BittensorError>
115where
116 S: subxt::tx::Signer<PolkadotConfig>,
117{
118 let dest_account =
119 AccountId::from_str(¶ms.dest).map_err(|_| BittensorError::InvalidHotkey {
120 hotkey: params.dest.clone(),
121 })?;
122
123 let dest_multi: subxt::utils::MultiAddress<AccountId, ()> =
124 subxt::utils::MultiAddress::Id(dest_account);
125
126 let call = api::tx()
127 .balances()
128 .transfer_keep_alive(dest_multi, params.amount_rao);
129
130 let tx_hash = client
131 .tx()
132 .sign_and_submit_default(&call, signer)
133 .await
134 .map_err(|e| BittensorError::TxSubmissionError {
135 message: format!("Failed to submit transfer: {}", e),
136 })?;
137
138 Ok(ExtrinsicResponse::success()
139 .with_message("Transfer completed successfully")
140 .with_extrinsic_hash(&format!("{:?}", tx_hash))
141 .with_data(Balance::from_rao(params.amount_rao)))
142}
143
144async fn transfer_allow_death<S>(
146 client: &OnlineClient<PolkadotConfig>,
147 signer: &S,
148 params: TransferParams,
149) -> Result<ExtrinsicResponse<Balance>, BittensorError>
150where
151 S: subxt::tx::Signer<PolkadotConfig>,
152{
153 let dest_account =
154 AccountId::from_str(¶ms.dest).map_err(|_| BittensorError::InvalidHotkey {
155 hotkey: params.dest.clone(),
156 })?;
157
158 let dest_multi: subxt::utils::MultiAddress<AccountId, ()> =
159 subxt::utils::MultiAddress::Id(dest_account);
160
161 let call = api::tx()
162 .balances()
163 .transfer_allow_death(dest_multi, params.amount_rao);
164
165 let tx_hash = client
166 .tx()
167 .sign_and_submit_default(&call, signer)
168 .await
169 .map_err(|e| BittensorError::TxSubmissionError {
170 message: format!("Failed to submit transfer: {}", e),
171 })?;
172
173 Ok(ExtrinsicResponse::success()
174 .with_message("Transfer completed successfully")
175 .with_extrinsic_hash(&format!("{:?}", tx_hash))
176 .with_data(Balance::from_rao(params.amount_rao)))
177}
178
179pub async fn transfer_all<S>(
183 client: &OnlineClient<PolkadotConfig>,
184 signer: &S,
185 dest: &str,
186 keep_alive: bool,
187) -> Result<ExtrinsicResponse<()>, BittensorError>
188where
189 S: subxt::tx::Signer<PolkadotConfig>,
190{
191 let dest_account = AccountId::from_str(dest).map_err(|_| BittensorError::InvalidHotkey {
192 hotkey: dest.to_string(),
193 })?;
194
195 let dest_multi: subxt::utils::MultiAddress<AccountId, ()> =
196 subxt::utils::MultiAddress::Id(dest_account);
197
198 let call = api::tx().balances().transfer_all(dest_multi, keep_alive);
199
200 let tx_hash = client
201 .tx()
202 .sign_and_submit_default(&call, signer)
203 .await
204 .map_err(|e| BittensorError::TxSubmissionError {
205 message: format!("Failed to submit transfer_all: {}", e),
206 })?;
207
208 Ok(ExtrinsicResponse::success()
209 .with_message("Transfer all completed")
210 .with_extrinsic_hash(&format!("{:?}", tx_hash))
211 .with_data(()))
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_transfer_params_tao() {
220 let params =
221 TransferParams::new_tao("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1.5);
222 assert_eq!(params.amount_rao, 1_500_000_000);
223 assert!(params.keep_alive);
224 }
225
226 #[test]
227 fn test_transfer_params_rao() {
228 let params =
229 TransferParams::new_rao("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1000);
230 assert_eq!(params.amount_rao, 1000);
231 }
232
233 #[test]
234 fn test_transfer_params_builder() {
235 let params =
236 TransferParams::new_tao("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1.0)
237 .keep_alive(false);
238
239 assert!(!params.keep_alive);
240 }
241}