index.dart 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:get_storage/get_storage.dart';
  4. import 'package:polkawallet_sdk/api/types/balanceData.dart';
  5. import 'package:polkawallet_sdk/api/types/networkParams.dart';
  6. import 'package:polkawallet_sdk/api/types/networkStateData.dart';
  7. import 'package:polkawallet_sdk/plugin/homeNavItem.dart';
  8. import 'package:polkawallet_sdk/plugin/store/balances.dart';
  9. import 'package:polkawallet_sdk/polkawallet_sdk.dart';
  10. import 'package:polkawallet_sdk/service/webViewRunner.dart';
  11. import 'package:polkawallet_sdk/storage/keyring.dart';
  12. import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
  13. import 'package:polkawallet_sdk/utils/app.dart';
  14. const String sdk_cache_key = 'polka_wallet_sdk_cache';
  15. const String net_state_cache_key = 'network_state';
  16. const String net_const_cache_key = 'network_const';
  17. const String balance_cache_key = 'balances';
  18. abstract class PolkawalletPlugin implements PolkawalletPluginBase {
  19. /// A plugin has a [WalletSDK] instance for connecting to it's node.
  20. final WalletSDK sdk = WalletSDK();
  21. /// Plugin should retrieve [balances] from sdk
  22. /// for display in Assets page of Polkawallet App.
  23. final balances = BalancesStore();
  24. /// Plugin should provide a list of defaultTokens
  25. /// for users of Polkawallet App.
  26. List<String> get defaultTokens => [];
  27. /// Plugin should provide a list of noneNativeToken
  28. /// for users of Polkawallet App.
  29. List<TokenBalanceData> get noneNativeTokensAll => [];
  30. final recoveryEnabled = false;
  31. final appUtils = AppUtils();
  32. /// The App will display this widget in assets page
  33. Widget? getAggregatedAssetsWidget(
  34. {String priceCurrency = 'USD',
  35. bool hideBalance = false,
  36. double rate = 1.0,
  37. @required Function? onSwitchBack,
  38. @required Function? onSwitchHideBalance}) =>
  39. null;
  40. /// The App will display this widget in native token detail page
  41. /// @param[transferType] = 0 | 1 | 2, (0 = all, 1 = in, 2 = out)
  42. Widget? getNativeTokenTransfers(
  43. {required String address, int transferType = 0}) =>
  44. null;
  45. /// Plugin should retrieve [networkState] & [networkConst] while start
  46. NetworkStateData get networkState {
  47. try {
  48. return NetworkStateData.fromJson(Map<String, dynamic>.from(
  49. _cache.read(_getNetworkCacheKey(net_state_cache_key)) ?? {}));
  50. } catch (err) {
  51. print(err);
  52. }
  53. return NetworkStateData();
  54. }
  55. Map get networkConst =>
  56. _cache.read(_getNetworkCacheKey(net_const_cache_key)) ?? {};
  57. GetStorage get _cache => GetStorage(sdk_cache_key);
  58. String _getNetworkCacheKey(String key) => '${key}_${basic.name}';
  59. String _getBalanceCacheKey(String? pubKey) =>
  60. '${balance_cache_key}_${basic.name}_$pubKey';
  61. Future<void> updateNetworkState() async {
  62. final state = await sdk.api.service.setting.queryNetwork();
  63. if (state != null) {
  64. _cache.write(_getNetworkCacheKey(net_const_cache_key), state["const"]);
  65. _cache.write(_getNetworkCacheKey(net_state_cache_key), state["props"]);
  66. }
  67. }
  68. void _updateBalances(KeyPairData acc, BalanceData data,
  69. {bool isFromCache = false}) {
  70. if (acc.address == data.accountId || isFromCache) {
  71. data.isFromCache = isFromCache;
  72. balances.setBalance(data);
  73. if (!isFromCache) {
  74. _cache.write(_getBalanceCacheKey(acc.pubKey), data.toJson());
  75. }
  76. }
  77. }
  78. /// This method will be called while user request to query balance.
  79. Future<void> updateBalances(KeyPairData acc) async {
  80. final data = await (sdk.api.account.queryBalance(acc.address)
  81. as FutureOr<BalanceData>);
  82. _updateBalances(acc, data);
  83. }
  84. void loadBalances(KeyPairData acc) {
  85. // do not load balance data from cache if we have no decimals data.
  86. if (networkState.tokenDecimals == null) return;
  87. _updateBalances(
  88. acc,
  89. BalanceData.fromJson(Map<String, dynamic>.from(
  90. _cache.read(_getBalanceCacheKey(acc.pubKey)) ??
  91. {'accountId': acc.address})),
  92. isFromCache: true);
  93. }
  94. /// This method will be called while App switched to a plugin.
  95. /// In this method, the plugin will init [WalletSDK] and start
  96. /// a webView for running `polkadot-js/api`.
  97. Future<void> beforeStart(
  98. Keyring keyring, {
  99. WebViewRunner? webView,
  100. String? jsCode,
  101. Function? socketDisconnectedAction,
  102. }) async {
  103. await sdk.init(keyring,
  104. webView: webView,
  105. jsCode: jsCode ?? (await loadJSCode()),
  106. socketDisconnectedAction: socketDisconnectedAction);
  107. await onWillStart(keyring);
  108. }
  109. /// This method will be called while App switched to a plugin.
  110. /// In this method, the plugin will:
  111. /// 1. connect to nodes.
  112. /// 2. retrieve network const & state.
  113. /// 3. subscribe balances & set balancesStore.
  114. Future<NetworkParams?> start(Keyring keyring,
  115. {List<NetworkParams>? nodes}) async {
  116. final res = await sdk.api.connectNode(keyring, nodes ?? nodeList);
  117. if (res == null) return null;
  118. keyring.setSS58(res.ss58);
  119. await updateNetworkState();
  120. if (keyring.current.address != null) {
  121. sdk.api.account.subscribeBalance(keyring.current.address,
  122. (BalanceData data) {
  123. _updateBalances(keyring.current, data);
  124. });
  125. }
  126. onStarted(keyring);
  127. return res;
  128. }
  129. /// This method will be called while App user changes account.
  130. void changeAccount(KeyPairData account) {
  131. sdk.api.account.unsubscribeBalance();
  132. loadBalances(account);
  133. sdk.api.account.subscribeBalance(account.address, (BalanceData data) {
  134. _updateBalances(account, data);
  135. });
  136. onAccountChanged(account);
  137. }
  138. /// This method will be called before plugin start
  139. Future<void> onWillStart(Keyring keyring) async {
  140. if (keyring.current.address != null) {
  141. loadBalances(keyring.current);
  142. }
  143. }
  144. /// This method will be called after plugin started
  145. Future<void> onStarted(Keyring keyring) async => null;
  146. /// This method will be called while App user changes account.
  147. /// In this method, the plugin should do:
  148. /// 1. update balance subscription to update balancesStore.
  149. /// 2. update other user state of plugin if needed.
  150. Future<void> onAccountChanged(KeyPairData account) async => null;
  151. /// we don't really need this method, calling webView.launch
  152. /// more than once will cause some exception.
  153. /// We just pass a [webViewParam] instance to the sdk.init function,
  154. /// so the sdk knows how to deal with the webView.
  155. Future<void> dispose() async {
  156. // do nothing
  157. }
  158. }
  159. abstract class PolkawalletPluginBase {
  160. /// A plugin's basic info, including: name, primaryColor and icons.
  161. final basic = PluginBasicData(
  162. name: 'kusama', primaryColor: Colors.black as MaterialColor?);
  163. /// Plugin should define a list of node to connect
  164. /// for users of Polkawallet App.
  165. List<NetworkParams> get nodeList => [];
  166. /// Plugin should provide [tokenIcons]
  167. /// for display in Assets page of Polkawallet App.
  168. final Map<String, Widget> tokenIcons = {};
  169. /// The [getNavItems] method returns a list of [HomeNavItem] which defines
  170. /// the [Widget] to be used in home page of polkawallet App.
  171. List<HomeNavItem> getNavItems(BuildContext context, Keyring keyring) => [];
  172. /// App will add plugin's pages with custom [routes].
  173. Map<String, WidgetBuilder> getRoutes(Keyring keyring) =>
  174. Map<String, WidgetBuilder>();
  175. /// App will inject plugin's [jsCode] into webview to connect.
  176. Future<String>? loadJSCode() => null;
  177. }
  178. class PluginBasicData {
  179. PluginBasicData({
  180. this.name,
  181. this.genesisHash,
  182. this.ss58,
  183. this.primaryColor,
  184. this.gradientColor,
  185. this.backgroundImage,
  186. this.icon,
  187. this.iconDisabled,
  188. this.jsCodeVersion,
  189. this.isTestNet = true,
  190. this.isXCMSupport = false,
  191. this.parachainId,
  192. });
  193. final String? name;
  194. final String? genesisHash;
  195. final int? ss58;
  196. final MaterialColor? primaryColor;
  197. final Color? gradientColor;
  198. /// The image will be displayed in network-select page
  199. final AssetImage? backgroundImage;
  200. /// The icons will be displayed in network-select page
  201. /// in Polkawallet App.
  202. final Widget? icon;
  203. final Widget? iconDisabled;
  204. /// JavaScript code version of your plugin.
  205. ///
  206. /// Polkawallet App will perform hot-update for the js code
  207. /// of your plugin with it.
  208. final int? jsCodeVersion;
  209. /// Your plugin is connected to a para-chain testNet by default.
  210. final bool isTestNet;
  211. /// Whether this para-chain receives assets from relay-chain.
  212. /// should set [parachainId] if [isXCMSupport] enabled.
  213. final bool isXCMSupport;
  214. final String? parachainId;
  215. }