polymesh_api_client_extras/
user.rs1use core::ops::{Deref, DerefMut};
2
3use polymesh_api::{
4 client::{dev, AccountId, DefaultSigner, IdentityId, Result, Signer},
5 polymesh::types::{
6 frame_system::AccountInfo, pallet_balances::AccountData,
7 polymesh_primitives::secondary_key::KeyRecord,
8 },
9 Api,
10};
11
12use crate::*;
13
14#[derive(Clone)]
15pub struct PolymeshHelper {
16 api: Api,
17 pub init_polyx: u128,
18 pub cdd: DefaultSigner,
19}
20
21impl PolymeshHelper {
22 pub async fn new(url: &str) -> Result<Self> {
23 let api = Api::new(url).await?;
24 Ok(Self {
25 api,
26 init_polyx: 100_000 * ONE_POLYX,
27 cdd: dev::alice(),
28 })
29 }
30
31 async fn batch_load_did_balance(&self, users: &mut [PolymeshUser]) -> Result<Vec<u128>> {
32 let mut balances = Vec::with_capacity(users.len());
33 for users in users.chunks_mut(200) {
34 let mut tasks = Vec::with_capacity(200);
35 for user in users.iter() {
36 let account = user.account();
37 let need_did = user.did.is_none();
38 let helper = self.clone();
39 let task = tokio::spawn(async move { helper.get_did_and_balance(account, need_did).await });
40 tasks.push(task);
41 }
42 for (idx, task) in tasks.into_iter().enumerate() {
43 let (did, balance) = task.await.unwrap()?;
44 if did.is_some() {
45 users[idx].did = did;
46 }
47 balances.push(balance);
48 }
49 }
50 Ok(balances)
51 }
52
53 async fn batch_load_dids(&self, users: &mut [PolymeshUser]) -> Result<()> {
54 for users in users.chunks_mut(200) {
55 let mut tasks = Vec::with_capacity(200);
56 for (idx, user) in users.iter().enumerate() {
57 let account = user.account();
58 let helper = self.clone();
59 if user.did.is_none() {
60 let task = tokio::spawn(async move { helper.get_did(account).await });
61 tasks.push((idx, task));
62 }
63 }
64 for (idx, task) in tasks {
65 let did = task.await.unwrap()?;
66 if did.is_some() {
67 users[idx].did = did;
68 }
69 }
70 }
71 Ok(())
72 }
73
74 pub async fn generate_named_users(&mut self, names: &[&str]) -> Result<Vec<PolymeshUser>> {
79 let mut users = Vec::with_capacity(names.len());
80 for name in names {
81 users.push(PolymeshUser::new(name)?);
83 }
84 self.onboard_users(&mut users).await?;
85 Ok(users)
86 }
87
88 pub async fn generate_prefix_users(
93 &mut self,
94 prefix: &str,
95 count: usize,
96 ) -> Result<Vec<PolymeshUser>> {
97 let mut users = Vec::with_capacity(count);
98 for idx in 0..count {
99 users.push(PolymeshUser::new(&format!("{prefix}_{idx}"))?);
101 }
102 self.onboard_users(&mut users).await?;
103 Ok(users)
104 }
105
106 async fn onboard_users(&mut self, users: &mut [PolymeshUser]) -> Result<()> {
107 let mut batches = Vec::new();
108 for users in users.chunks_mut(200) {
109 let balances = self.batch_load_did_balance(users).await?;
110 let mut did_calls = Vec::new();
112 let mut fund_calls = Vec::new();
113 for (idx, user) in users.iter_mut().enumerate() {
114 let account = user.account();
115 if user.did.is_none() {
117 did_calls.push(
119 self
120 .api
121 .call()
122 .identity()
123 .cdd_register_did_with_cdd(account, vec![], None)?
124 .into(),
125 );
126 }
127 let balance = balances[idx];
129 if balance < self.init_polyx {
130 fund_calls.push(
132 self
133 .api
134 .call()
135 .balances()
136 .set_balance(account.into(), self.init_polyx, 0)?
137 .into(),
138 );
139 }
140 }
141 if did_calls.len() > 0 {
142 let res = self
143 .api
144 .call()
145 .utility()
146 .batch(did_calls)?
147 .submit_and_watch(&mut self.cdd)
148 .await?;
149 batches.push(res);
150 }
151 if fund_calls.len() > 0 {
152 let res = self
153 .api
154 .call()
155 .sudo()
156 .sudo(self.api.call().utility().batch(fund_calls)?.into())?
157 .submit_and_watch(&mut self.cdd)
158 .await?;
159 batches.push(res);
160 }
161 }
162 if batches.len() == 0 {
164 return Ok(());
165 }
166 for mut res in batches {
168 res.wait_finalized().await?;
169 }
170 self.batch_load_dids(users).await?;
172 Ok(())
173 }
174
175 pub async fn key_records(&self, account: AccountId) -> Result<Option<KeyRecord<AccountId>>> {
176 Ok(self.api.query().identity().key_records(account).await?)
177 }
178
179 pub async fn get_did(&self, account: AccountId) -> Result<Option<IdentityId>> {
180 let did = match self.key_records(account).await? {
181 Some(KeyRecord::PrimaryKey(did)) => Some(did),
182 Some(KeyRecord::SecondaryKey(did)) => Some(did),
183 _ => None,
184 };
185 Ok(did)
186 }
187
188 #[cfg(not(feature = "polymesh_v8"))]
189 pub async fn get_account_info(
190 &self,
191 account: AccountId,
192 ) -> Result<AccountInfo<u32, AccountData>> {
193 Ok(self.api.query().system().account(account).await?)
194 }
195
196 #[cfg(feature = "polymesh_v8")]
197 pub async fn get_account_info(
198 &self,
199 account: AccountId,
200 ) -> Result<AccountInfo<u32, AccountData<u128>>> {
201 Ok(self.api.query().system().account(account).await?)
202 }
203
204 pub async fn get_account_balance(&self, account: AccountId) -> Result<u128> {
205 self
206 .get_account_info(account)
207 .await
208 .map(|info| info.data.free)
209 }
210
211 async fn get_did_and_balance(
212 &self,
213 account: AccountId,
214 need_did: bool,
215 ) -> Result<(Option<IdentityId>, u128)> {
216 let did: Option<IdentityId> = if need_did {
217 self.get_did(account).await?
218 } else {
219 None
220 };
221 let balance = self.get_account_balance(account).await?;
222 Ok((did, balance))
223 }
224
225 pub async fn register_and_fund(&mut self, account: AccountId) -> Result<IdentityId> {
226 let did = match self.get_did(account).await? {
227 Some(did) => did,
228 None => {
229 let mut res = self
232 .api
233 .call()
234 .utility()
235 .batch(vec![
236 self
237 .api
238 .call()
239 .identity()
240 .cdd_register_did_with_cdd(account, vec![], None)?
241 .into(),
242 self
243 .api
244 .call()
245 .balances()
246 .transfer(account.into(), self.init_polyx)?
247 .into(),
248 ])?
249 .execute(&mut self.cdd)
250 .await?;
251 get_identity_id(&mut res).await?.unwrap()
252 }
253 };
254 Ok(did)
255 }
256}
257
258#[derive(Clone)]
259pub struct PolymeshUser {
260 pub name: String,
261 signer: DefaultSigner,
262 pub did: Option<IdentityId>,
263}
264
265impl Deref for PolymeshUser {
266 type Target = DefaultSigner;
267
268 fn deref(&self) -> &Self::Target {
269 &self.signer
270 }
271}
272
273impl DerefMut for PolymeshUser {
274 fn deref_mut(&mut self) -> &mut Self::Target {
275 &mut self.signer
276 }
277}
278
279impl PolymeshUser {
280 pub fn new(name: &str) -> Result<Self> {
281 Ok(Self {
282 name: name.to_string(),
283 signer: DefaultSigner::from_string(&format!("//{name}"), None)?,
284 did: None,
285 })
286 }
287
288 pub fn from_signer(name: &str, signer: DefaultSigner) -> Self {
289 Self {
290 name: name.to_string(),
291 signer,
292 did: None,
293 }
294 }
295}