apiKeyring.dart 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import 'dart:async';
  2. import 'package:polkawallet_sdk/api/api.dart';
  3. import 'package:polkawallet_sdk/api/types/addressIconData.dart';
  4. import 'package:polkawallet_sdk/api/types/verifyResult.dart';
  5. import 'package:polkawallet_sdk/service/keyring.dart';
  6. import 'package:polkawallet_sdk/storage/keyring.dart';
  7. import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
  8. import 'package:polkawallet_sdk/webviewWithExtension/types/signExtrinsicParam.dart';
  9. enum KeyType { mnemonic, rawSeed, keystore }
  10. enum CryptoType { sr25519, ed25519 }
  11. /// Keyring API manages keyPairs for through `polkadot-js/keyring`
  12. class ApiKeyring {
  13. ApiKeyring(this.apiRoot, this.service);
  14. final PolkawalletApi apiRoot;
  15. final ServiceKeyring? service;
  16. /// Generate a set of new mnemonic.
  17. Future<AddressIconDataWithMnemonic> generateMnemonic(int ss58,
  18. {CryptoType cryptoType = CryptoType.sr25519,
  19. String derivePath = '',
  20. String key = ''}) async {
  21. final mnemonicData = await service!.generateMnemonic(ss58,
  22. cryptoType: cryptoType, derivePath: derivePath, key: key);
  23. return mnemonicData;
  24. }
  25. /// get address and avatar from mnemonic.
  26. Future<AddressIconData> addressFromMnemonic(int ss58,
  27. {CryptoType cryptoType = CryptoType.sr25519,
  28. String derivePath = '',
  29. required String mnemonic}) async {
  30. final addressInfo = await service!.addressFromMnemonic(ss58,
  31. cryptoType: cryptoType, derivePath: derivePath, mnemonic: mnemonic);
  32. return addressInfo;
  33. }
  34. /// get address and avatar from rawSeed.
  35. Future<AddressIconData> addressFromRawSeed(int ss58,
  36. {CryptoType cryptoType = CryptoType.sr25519,
  37. String derivePath = '',
  38. required String rawSeed}) async {
  39. final addressInfo = await service!.addressFromRawSeed(ss58,
  40. cryptoType: cryptoType, derivePath: derivePath, rawSeed: rawSeed);
  41. return addressInfo;
  42. }
  43. /// get address and avatar from KeyStore.
  44. Future<AddressIconData> addressFromKeyStore(int ss58,
  45. {required Map keyStore}) async {
  46. final addressInfo =
  47. await service!.addressFromKeyStore(ss58, keyStore: keyStore);
  48. return AddressIconData.fromJson({
  49. 'address': addressInfo[0][0],
  50. 'svg': addressInfo[0][1],
  51. });
  52. }
  53. /// check mnemonic valid.
  54. Future<bool> checkMnemonicValid(String mnemonic) async {
  55. return service!.checkMnemonicValid(mnemonic);
  56. }
  57. /// Import account from mnemonic/rawSeed/keystore.
  58. /// param [cryptoType] can be `sr25519`(default) or `ed25519`.
  59. /// throw error if import failed.
  60. /// return null if keystore password check failed.
  61. Future<Map?> importAccount(
  62. Keyring keyring, {
  63. required KeyType keyType,
  64. required String key,
  65. required String name,
  66. required String password,
  67. CryptoType cryptoType = CryptoType.sr25519,
  68. String derivePath = '',
  69. }) async {
  70. final dynamic? acc = await service!.importAccount(
  71. keyType: keyType,
  72. key: key,
  73. name: name,
  74. password: password,
  75. cryptoType: cryptoType,
  76. derivePath: derivePath,
  77. );
  78. if (acc == null) {
  79. return null;
  80. }
  81. if (acc['error'] != null) {
  82. throw Exception(acc['error']);
  83. }
  84. return acc;
  85. }
  86. /// Add account to local storage.
  87. Future<KeyPairData> addAccount(
  88. Keyring keyring, {
  89. required KeyType keyType,
  90. required Map acc,
  91. required String password,
  92. }) async {
  93. // save seed and remove it before add account
  94. if (keyType == KeyType.mnemonic || keyType == KeyType.rawSeed) {
  95. final String type = keyType.toString().split('.')[1];
  96. final String? seed = acc[type];
  97. if (seed != null && seed.isNotEmpty) {
  98. keyring.store
  99. .encryptSeedAndSave(acc['pubKey'], acc[type], type, password);
  100. acc.remove(type);
  101. }
  102. }
  103. // save keystore to storage
  104. await keyring.store.addAccount(acc);
  105. await updatePubKeyIconsMap(keyring, [acc['pubKey']]);
  106. updatePubKeyAddressMap(keyring);
  107. updateIndicesMap(keyring, [acc['address']]);
  108. return KeyPairData.fromJson(acc as Map<String, dynamic>);
  109. }
  110. /// Add a contact.
  111. Future<KeyPairData> addContact(Keyring keyring, Map acc) async {
  112. final pubKey = await (service!.serviceRoot.account
  113. .decodeAddress([acc['address']]) as FutureOr<Map<dynamic, dynamic>>);
  114. acc['pubKey'] = pubKey.keys.toList()[0];
  115. // save keystore to storage
  116. await keyring.store.addContact(acc);
  117. await updatePubKeyAddressMap(keyring);
  118. await updatePubKeyIconsMap(keyring, [acc['pubKey']]);
  119. updateIndicesMap(keyring, [acc['address']]);
  120. return keyring.contacts.firstWhere((e) => e.pubKey == acc['pubKey']);
  121. }
  122. /// Every time we change the keyPairs, we need to update the
  123. /// pubKey-address map.
  124. Future<void> updatePubKeyAddressMap(Keyring keyring) async {
  125. final ls = keyring.store.list.toList();
  126. ls.addAll(keyring.store.contacts);
  127. // get new addresses from webView.
  128. final res = await service!.getPubKeyAddressMap(ls, keyring.store.ss58List);
  129. // set new addresses to Keyring instance.
  130. if (res != null && res[keyring.ss58.toString()] != null) {
  131. keyring.store.updatePubKeyAddressMap(Map<String, Map>.from(res));
  132. }
  133. }
  134. /// This method query account icons and set icons to [Keyring.store]
  135. /// so we can get icon of an account from [Keyring] instance.
  136. Future<void> updatePubKeyIconsMap(Keyring keyring, [List? pubKeys]) async {
  137. final List<String?> ls = [];
  138. if (pubKeys != null) {
  139. ls.addAll(List<String>.from(pubKeys));
  140. } else {
  141. ls.addAll(keyring.keyPairs.map((e) => e.pubKey).toList());
  142. ls.addAll(keyring.contacts.map((e) => e.pubKey).toList());
  143. }
  144. if (ls.length == 0) return;
  145. // get icons from webView.
  146. final res = await service!.getPubKeyIconsMap(ls);
  147. // set new icons to Keyring instance.
  148. if (res != null) {
  149. final data = {};
  150. res.forEach((e) {
  151. data[e[0]] = e[1];
  152. });
  153. keyring.store.updateIconsMap(Map<String, String>.from(data));
  154. }
  155. }
  156. /// This method query account indices and set data to [Keyring.store]
  157. /// so we can get index info of an account from [Keyring] instance.
  158. Future<void> updateIndicesMap(Keyring keyring, [List? addresses]) async {
  159. final List<String?> ls = [];
  160. if (addresses != null) {
  161. ls.addAll(List<String>.from(addresses));
  162. } else {
  163. ls.addAll(keyring.allWithContacts.map((e) => e.address).toList());
  164. }
  165. if (ls.length == 0) return;
  166. // get account indices from webView.
  167. final res = await apiRoot.account.queryIndexInfo(ls);
  168. // set new indices to Keyring instance.
  169. if (res != null) {
  170. final data = {};
  171. res.forEach((e) {
  172. data[e['accountId']] = e;
  173. });
  174. keyring.store.updateIndicesMap(Map<String, Map>.from(data));
  175. keyring.allAccounts;
  176. }
  177. }
  178. /// Decrypt and get the backup of seed.
  179. Future<SeedBackupData?> getDecryptedSeed(Keyring keyring, password) async {
  180. final Map? data =
  181. await keyring.store.getDecryptedSeed(keyring.current.pubKey, password);
  182. if (data == null) {
  183. return null;
  184. }
  185. if (data['seed'] == null) {
  186. data['error'] = 'wrong password';
  187. }
  188. return SeedBackupData.fromJson(data as Map<String, dynamic>);
  189. }
  190. /// delete account from storage
  191. Future<void> deleteAccount(Keyring keyring, KeyPairData account) async {
  192. if (account != null) {
  193. await keyring.store.deleteAccount(account.pubKey);
  194. }
  195. }
  196. /// check password of account
  197. Future<bool> checkPassword(KeyPairData account, String pass) async {
  198. final res = await service!.checkPassword(account.pubKey, pass);
  199. return res;
  200. }
  201. /// change password of account
  202. Future<KeyPairData?> changePassword(
  203. Keyring keyring, String passOld, passNew) async {
  204. final acc = keyring.current;
  205. // 1. change password of keyPair in webView
  206. final res = await service!.changePassword(acc.pubKey, passOld, passNew);
  207. if (res == null) {
  208. return null;
  209. }
  210. // 2. if success in webView, then update encrypted seed in local storage.
  211. keyring.store.updateEncryptedSeed(acc.pubKey, passOld, passNew);
  212. // update json meta data
  213. service!.updateKeyPairMetaData(res, acc.name);
  214. // update keyPair date in storage
  215. keyring.store.updateAccount(res);
  216. return KeyPairData.fromJson(res as Map<String, dynamic>);
  217. }
  218. /// change name of account
  219. Future<KeyPairData> changeName(Keyring keyring, String name) async {
  220. final json = keyring.current.toJson();
  221. // update json meta data
  222. service!.updateKeyPairMetaData(json, name);
  223. // update keyPair date in storage
  224. keyring.store.updateAccount(json);
  225. return KeyPairData.fromJson(json);
  226. }
  227. /// Check if derive path is valid, return [null] if valid,
  228. /// and return error message if invalid.
  229. Future<String?> checkDerivePath(
  230. String seed, path, CryptoType cryptoType) async {
  231. String? res = await service!.checkDerivePath(seed, path, cryptoType);
  232. return res;
  233. }
  234. /// Open a new webView for a DApp,
  235. /// sign extrinsic or msg for the DApp.
  236. Future<ExtensionSignResult?> signAsExtension(
  237. String password, SignAsExtensionParam param) async {
  238. final signature = await service!.signAsExtension(password, param.toJson());
  239. if (signature == null) {
  240. return null;
  241. }
  242. final ExtensionSignResult res = ExtensionSignResult();
  243. res.id = param.id;
  244. res.signature = signature['signature'];
  245. return res;
  246. }
  247. Future<VerifyResult?> signatureVerify(
  248. String message, signature, address) async {
  249. final res = await service!.signatureVerify(message, signature, address);
  250. if (res == null) {
  251. return null;
  252. }
  253. return VerifyResult.fromJson(
  254. Map<String, dynamic>.of(res as Map<String, dynamic>));
  255. }
  256. }