apiEthers.dart 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:math';
  4. import 'package:bip39/bip39.dart' as bip39;
  5. import 'package:ethers/signers/wallet.dart' as ethers;
  6. import 'package:polkawallet_sdk/api/types/addressIconData.dart';
  7. import 'package:polkawallet_sdk/service/index.dart';
  8. import 'package:polkawallet_sdk/storage/keyringEVM.dart';
  9. import 'package:polkawallet_sdk/storage/types/ethWalletData.dart';
  10. import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
  11. import 'package:web3dart/web3dart.dart';
  12. enum EVMKeyType { mnemonic, privateKey, keystore }
  13. const crypt_n = 1 << 14;
  14. /// KeyringEVM API manages EVM keyPairs
  15. class ApiEthers {
  16. ApiEthers(this.service);
  17. final SubstrateService? service;
  18. /// Generate a set of new mnemonic.
  19. Future<AddressIconDataWithMnemonic> generateMnemonic(
  20. {String derivePath = '', String key = ''}) async {
  21. final mnemonic = bip39.generateMnemonic();
  22. final wallet = ethers.Wallet.fromMnemonic(mnemonic);
  23. return AddressIconDataWithMnemonic.fromJson({
  24. 'mnemonic': mnemonic,
  25. 'address': wallet.address,
  26. 'svg': 'xxx',
  27. });
  28. }
  29. /// get address and avatar from mnemonic.
  30. Future<AddressIconData> addressFromMnemonic(String mnemonic,
  31. {String derivePath = ''}) async {
  32. final wallet = ethers.Wallet.fromMnemonic(mnemonic);
  33. return AddressIconData.fromJson({'address': wallet.address, 'svg': 'xxx'});
  34. }
  35. /// get address and avatar from privateKey.
  36. Future<AddressIconData> addressFromRawSeed(String privateKey,
  37. {String derivePath = ''}) async {
  38. final wallet = ethers.Wallet.fromPrivateKey(privateKey);
  39. return AddressIconData.fromJson({'address': wallet.address, 'svg': 'xxx'});
  40. }
  41. /// check mnemonic valid.
  42. Future<bool> checkMnemonicValid(String mnemonic) async {
  43. return bip39.validateMnemonic(mnemonic);
  44. }
  45. /// Import account from mnemonic/rawSeed/keystore and we get a JSON object.
  46. /// param [cryptoType] can be `sr25519`(default) or `ed25519`.
  47. /// throw error if import failed.
  48. /// return null if keystore password check failed.
  49. Future<Map<String, dynamic>> importAccount({
  50. required EVMKeyType keyType,
  51. required String key,
  52. required String name,
  53. required String password,
  54. String derivePath = '',
  55. }) async {
  56. Wallet web3Wallet;
  57. switch (keyType) {
  58. case EVMKeyType.mnemonic:
  59. final wallet = ethers.Wallet.fromMnemonic(key);
  60. final credential = EthPrivateKey.fromHex(wallet.privateKey!);
  61. web3Wallet = Wallet.createNew(credential, password, Random.secure(),
  62. scryptN: crypt_n);
  63. break;
  64. case EVMKeyType.privateKey:
  65. final credential = EthPrivateKey.fromHex(key);
  66. web3Wallet = Wallet.createNew(credential, password, Random.secure(),
  67. scryptN: crypt_n);
  68. break;
  69. case EVMKeyType.keystore:
  70. web3Wallet = Wallet.fromJson(key, password);
  71. }
  72. final walletJson = jsonDecode(web3Wallet.toJson());
  73. final type = keyType.toString().split('.')[1];
  74. return {
  75. ...walletJson,
  76. 'address': web3Wallet.privateKey.address.hexEip55,
  77. 'name': name,
  78. type: key,
  79. };
  80. }
  81. /// Add account to local storage.
  82. Future<EthWalletData> addAccount(
  83. KeyringEVM keyring, {
  84. required EVMKeyType keyType,
  85. required Map<String, dynamic> acc,
  86. required String password,
  87. }) async {
  88. // save seed and remove it before add account
  89. final type = keyType.toString().split('.')[1];
  90. if (keyType == EVMKeyType.mnemonic || keyType == EVMKeyType.privateKey) {
  91. final String? seed = acc[type];
  92. if (seed != null && seed.isNotEmpty) {
  93. keyring.store
  94. .encryptSeedAndSave(acc['address'], acc[type], type, password);
  95. }
  96. }
  97. acc.remove(type);
  98. // save keystore to storage
  99. await keyring.store.addAccount(acc);
  100. await updateIconsMap(keyring, [acc['address']]);
  101. return EthWalletData.fromJson(acc);
  102. }
  103. /// Add a contact.
  104. Future<EthWalletData> addContact(KeyringEVM keyring, Map acc) async {
  105. // save keystore to storage
  106. await keyring.store.addContact(acc);
  107. await updateIconsMap(keyring, [acc['address']]);
  108. return keyring.contacts.firstWhere((e) => e.address == acc['address']);
  109. }
  110. /// This method query account icons and set icons to [KeyringEVM.store]
  111. /// so we can get icon of an account from [KeyringEVM] instance.
  112. Future<void> updateIconsMap(KeyringEVM keyring, [List? addresses]) async {
  113. // final List<String?> ls = [];
  114. // if (addresses != null) {
  115. // ls.addAll(List<String>.from(addresses));
  116. // } else {
  117. // ls.addAll(keyring.keyPairs.map((e) => e.address).toList());
  118. // ls.addAll(keyring.contacts.map((e) => e.address).toList());
  119. // }
  120. //
  121. // if (ls.length == 0) return;
  122. // // get icons from webView.
  123. // final res = await service!.getPubKeyIconsMap(ls);
  124. // // set new icons to Keyring instance.
  125. // if (res != null) {
  126. // final data = {};
  127. // res.forEach((e) {
  128. // data[e[0]] = e[1];
  129. // });
  130. // keyring.store.updateIconsMap(Map<String, String>.from(data));
  131. // }
  132. }
  133. /// Decrypt and get the backup of seed.
  134. Future<bool> checkEncryptedSeedExist(
  135. KeyringEVM keyring, EthWalletData acc, EVMKeyType keyType) async {
  136. return keyring.store.checkSeedExist(keyType, acc.address ?? '');
  137. }
  138. /// Decrypt and get the backup of seed.
  139. Future<SeedBackupData?> getDecryptedSeed(
  140. KeyringEVM keyring, String password) async {
  141. final Map? data =
  142. await keyring.store.getDecryptedSeed(keyring.current.address, password);
  143. if (data == null) {
  144. return null;
  145. }
  146. if (data['seed'] == null) {
  147. data['error'] = 'wrong password';
  148. }
  149. return SeedBackupData.fromJson(data as Map<String, dynamic>);
  150. }
  151. /// delete account from storage
  152. Future<void> deleteAccount(KeyringEVM keyring, EthWalletData account) async {
  153. await keyring.store.deleteAccount(account.address);
  154. }
  155. /// check password of account
  156. Future<bool> checkPassword(EthWalletData account, String pass) async {
  157. try {
  158. Wallet.fromJson(jsonEncode(account.toJson()), pass);
  159. return true;
  160. } catch (err) {
  161. print(err.toString());
  162. // ignore
  163. }
  164. return false;
  165. }
  166. /// change password of account
  167. Future<EthWalletData?> changePassword(
  168. KeyringEVM keyring, String passOld, passNew) async {
  169. final acc = keyring.current;
  170. // 1. check old password
  171. final res = await checkPassword(acc, passOld);
  172. if (!res) {
  173. return null;
  174. }
  175. final wallet = Wallet.fromJson(jsonEncode(acc.toJson()), passOld);
  176. final walletNew = Wallet.createNew(
  177. wallet.privateKey, passNew, Random.secure(),
  178. scryptN: crypt_n);
  179. final walletJson = jsonDecode(walletNew.toJson());
  180. final accNew = EthWalletData()
  181. ..address = acc.address
  182. ..name = acc.name
  183. ..memo = acc.memo
  184. ..observation = acc.observation
  185. ..id = walletJson['id']
  186. ..version = walletJson['version']
  187. ..crypto = walletJson['crypto'];
  188. // 2. then update encrypted seed in local storage.
  189. keyring.store.updateEncryptedSeed(acc.address, passOld, passNew);
  190. // 3. update keyPair data in storage
  191. keyring.store.updateAccount(accNew.toJson());
  192. return accNew;
  193. }
  194. /// change name of account
  195. Future<KeyPairData> changeName(KeyringEVM keyring, String name) async {
  196. final json = keyring.current.toJson();
  197. json['name'] = name;
  198. // update keyPair date in storage
  199. keyring.store.updateAccount(json);
  200. return KeyPairData.fromJson(json);
  201. }
  202. // /// Open a new webView for a DApp,
  203. // /// sign extrinsic or msg for the DApp.
  204. // Future<ExtensionSignResult?> signAsExtension(
  205. // String password, SignAsExtensionParam param) async {
  206. // final signature = await service!.signAsExtension(password, param.toJson());
  207. // if (signature == null) {
  208. // return null;
  209. // }
  210. // final ExtensionSignResult res = ExtensionSignResult();
  211. // res.id = param.id;
  212. // res.signature = signature['signature'];
  213. // return res;
  214. // }
  215. //
  216. // Future<VerifyResult?> signatureVerify(
  217. // String message, signature, address) async {
  218. // final res = await service!.signatureVerify(message, signature, address);
  219. // if (res == null) {
  220. // return null;
  221. // }
  222. // return VerifyResult.fromJson(
  223. // Map<String, dynamic>.of(res as Map<String, dynamic>));
  224. // }
  225. }