123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- import 'dart:async';
- import 'package:flutter/material.dart';
- import 'package:get_storage/get_storage.dart';
- import 'package:polkawallet_sdk/api/types/balanceData.dart';
- import 'package:polkawallet_sdk/api/types/networkParams.dart';
- import 'package:polkawallet_sdk/api/types/networkStateData.dart';
- import 'package:polkawallet_sdk/plugin/homeNavItem.dart';
- import 'package:polkawallet_sdk/plugin/store/balances.dart';
- import 'package:polkawallet_sdk/polkawallet_sdk.dart';
- import 'package:polkawallet_sdk/service/webViewRunner.dart';
- import 'package:polkawallet_sdk/storage/keyring.dart';
- import 'package:polkawallet_sdk/storage/keyringEVM.dart';
- import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
- import 'package:polkawallet_sdk/utils/app.dart';
- const String sdk_cache_key = 'polka_wallet_sdk_cache';
- const String net_state_cache_key = 'network_state';
- const String net_const_cache_key = 'network_const';
- const String balance_cache_key = 'balances';
- abstract class PolkawalletPlugin implements PolkawalletPluginBase {
- /// A plugin has a [WalletSDK] instance for connecting to it's node.
- final WalletSDK sdk = WalletSDK();
- /// Plugin should retrieve [balances] from sdk
- /// for display in Assets page of Polkawallet App.
- final balances = BalancesStore();
- /// Plugin should provide a list of defaultTokens
- /// for users of Polkawallet App.
- List<String> get defaultTokens => [];
- /// Plugin should provide a list of noneNativeToken
- /// for users of Polkawallet App.
- List<TokenBalanceData> get noneNativeTokensAll => [];
- final recoveryEnabled = false;
- final appUtils = AppUtils();
- /// The App will display this widget in assets page
- Widget? getAggregatedAssetsWidget(
- {String priceCurrency = 'USD',
- bool hideBalance = false,
- double rate = 1.0,
- @required Function? onSwitchBack,
- @required Function? onSwitchHideBalance}) =>
- null;
- /// The App will display this widget in native token detail page
- /// @param[transferType] = 0 | 1 | 2, (0 = all, 1 = in, 2 = out)
- Widget? getNativeTokenTransfers(
- {required String address, int transferType = 0}) =>
- null;
- /// Plugin should retrieve [networkState] & [networkConst] while start
- NetworkStateData get networkState {
- try {
- return NetworkStateData.fromJson(Map<String, dynamic>.from(
- _cache.read(_getNetworkCacheKey(net_state_cache_key)) ?? {}));
- } catch (err) {
- print(err);
- }
- return NetworkStateData();
- }
- Map get networkConst =>
- _cache.read(_getNetworkCacheKey(net_const_cache_key)) ?? {};
- GetStorage get _cache => GetStorage(sdk_cache_key);
- String _getNetworkCacheKey(String key) => '${key}_${basic.name}';
- String _getBalanceCacheKey(String? pubKey) =>
- '${balance_cache_key}_${basic.name}_$pubKey';
- Future<void> updateNetworkState() async {
- final state = await sdk.api.service.setting.queryNetwork();
- if (state != null) {
- _cache.write(_getNetworkCacheKey(net_const_cache_key), state["const"]);
- _cache.write(_getNetworkCacheKey(net_state_cache_key), state["props"]);
- }
- }
- void _updateBalances(KeyPairData acc, BalanceData data,
- {bool isFromCache = false}) {
- if (acc.address == data.accountId || isFromCache) {
- data.isFromCache = isFromCache;
- balances.setBalance(data);
- if (!isFromCache) {
- _cache.write(_getBalanceCacheKey(acc.pubKey), data.toJson());
- }
- }
- }
- /// This method will be called while user request to query balance.
- Future<void> updateBalances(KeyPairData acc) async {
- if (acc.pubKey == acc.address) {
- //eth
- final data =
- await sdk.api.eth.account.getNativeTokenBalance(acc.address ?? '');
- _updateBalances(
- acc,
- BalanceData()
- ..accountId = acc.address
- ..freeBalance = data
- ..availableBalance = data
- ..lockedBalance = '0'
- ..reservedBalance = '0');
- } else {
- final data = await (sdk.api.account.queryBalance(acc.address));
- if (data == null) {
- print(
- '\n\n\n\nERROR PolkawalletPlugin.updateBalances sdk.api.account.queryBalance returned null\n\n\n\n');
- throw Exception(
- '(sdk.api.account.queryBalance(acc.address) returned null');
- } else {
- _updateBalances(acc, data);
- }
- }
- }
- void loadBalances(KeyPairData acc) {
- // do not load balance data from cache if we have no decimals data.
- if (networkState.tokenDecimals == null) return;
- _updateBalances(
- acc,
- BalanceData.fromJson(Map<String, dynamic>.from(
- _cache.read(_getBalanceCacheKey(acc.pubKey)) ??
- {'accountId': acc.address})),
- isFromCache: true);
- }
- /// This method will be called while App switched to a plugin.
- /// In this method, the plugin will init [WalletSDK] and start
- /// a webView for running `polkadot-js/api`.
- Future<void> beforeStart(
- Keyring keyring, {
- KeyringEVM? keyringEVM,
- WebViewRunner? webView,
- String? jsCode,
- Function? socketDisconnectedAction,
- bool isEVM = false,
- }) async {
- await sdk.init(keyring,
- keyringEVM: keyringEVM,
- webView: webView,
- jsCode: jsCode ?? (await loadJSCode()),
- socketDisconnectedAction: socketDisconnectedAction,
- isEVM: isEVM);
- await (isEVM ? onWillStartEVM(keyringEVM!) : onWillStart(keyring));
- }
- /// This method will be called while App switched to a plugin.
- /// In this method, the plugin will:
- /// 1. connect to nodes.
- /// 2. retrieve network const & state.
- /// 3. subscribe balances & set balancesStore.
- Future<NetworkParams?> start(Keyring keyring,
- {List<NetworkParams>? nodes,
- KeyringEVM? keyringEVM,
- NetworkParams? nodeEVM}) async {
- if (nodeEVM == null) {
- final res = await sdk.api.connectNode(keyring, nodes ?? nodeList);
- if (res == null) return null;
- keyring.setSS58(res.ss58);
- await updateNetworkState();
- if (keyring.current.address != null) {
- sdk.api.account.subscribeBalance(keyring.current.address,
- (BalanceData data) {
- _updateBalances(keyring.current, data);
- });
- }
- onStarted(keyring);
- return res;
- }
- final evmRes = await sdk.api.connectEVM(nodeEVM);
- if (evmRes == null) return null;
- if (keyringEVM?.current.address != null) {
- final data = await sdk.api.eth.account
- .getNativeTokenBalance(keyringEVM?.current.address ?? '');
- _updateBalances(
- keyringEVM!.current.toKeyPairData(),
- BalanceData()
- ..accountId = keyringEVM.current.address
- ..freeBalance = data
- ..availableBalance = data
- ..lockedBalance = '0'
- ..reservedBalance = '0');
- }
- onStartedEVM(keyringEVM!);
- return evmRes;
- }
- /// This method will be called while App user changes account.
- Future<void> changeAccount(KeyPairData account) async {
- onAccountChanged(account);
- if (account.pubKey == account.address) {
- //eth
- final data = await sdk.api.eth.account
- .getNativeTokenBalance(account.address ?? '');
- _updateBalances(
- account,
- BalanceData()
- ..accountId = account.address
- ..freeBalance = data
- ..availableBalance = data
- ..lockedBalance = '0'
- ..reservedBalance = '0');
- } else {
- sdk.api.account.unsubscribeBalance();
- loadBalances(account);
- sdk.api.account.subscribeBalance(account.address, (BalanceData data) {
- _updateBalances(account, data);
- });
- }
- }
- /// This method will be called before plugin start
- Future<void> onWillStart(Keyring keyring) async {
- if (keyring.current.address != null) {
- loadBalances(keyring.current);
- }
- }
- Future<void> onWillStartEVM(KeyringEVM keyring) async {
- if (keyring.current.address != null) {
- loadBalances(keyring.current.toKeyPairData());
- }
- }
- /// This method will be called after plugin started
- Future<void> onStarted(Keyring keyring) async => null;
- /// This method will be called after plugin started
- Future<void> onStartedEVM(KeyringEVM keyringEvm) async => null;
- /// This method will be called while App user changes account.
- /// In this method, the plugin should do:
- /// 1. update balance subscription to update balancesStore.
- /// 2. update other user state of plugin if needed.
- Future<void> onAccountChanged(KeyPairData account) async => null;
- /// we don't really need this method, calling webView.launch
- /// more than once will cause some exception.
- /// We just pass a [webViewParam] instance to the sdk.init function,
- /// so the sdk knows how to deal with the webView.
- Future<void> dispose() async {
- // do nothing
- }
- }
- abstract class PolkawalletPluginBase {
- /// A plugin's basic info, including: name, primaryColor and icons.
- final basic = PluginBasicData(
- name: 'kusama', primaryColor: Colors.black as MaterialColor?);
- /// Plugin should define a list of node to connect
- /// for users of Polkawallet App.
- List<NetworkParams> get nodeList => [];
- /// Plugin should provide [tokenIcons]
- /// for display in Assets page of Polkawallet App.
- final Map<String, Widget> tokenIcons = {};
- /// The [getNavItems] method returns a list of [HomeNavItem] which defines
- /// the [Widget] to be used in home page of polkawallet App.
- List<HomeNavItem> getNavItems(BuildContext context, Keyring keyring) => [];
- /// App will add plugin's pages with custom [routes].
- Map<String, WidgetBuilder> getRoutes(Keyring keyring) =>
- Map<String, WidgetBuilder>();
- /// App will inject plugin's [jsCode] into webview to connect.
- Future<String>? loadJSCode() => null;
- }
- class PluginBasicData {
- PluginBasicData({
- this.name,
- this.genesisHash,
- this.ss58,
- this.primaryColor,
- this.gradientColor,
- this.backgroundImage,
- this.icon,
- this.iconDisabled,
- this.jsCodeVersion,
- this.isTestNet = true,
- this.isXCMSupport = false,
- this.parachainId,
- });
- final String? name;
- final String? genesisHash;
- final int? ss58;
- final MaterialColor? primaryColor;
- final Color? gradientColor;
- /// The image will be displayed in network-select page
- final AssetImage? backgroundImage;
- /// The icons will be displayed in network-select page
- /// in Polkawallet App.
- final Widget? icon;
- final Widget? iconDisabled;
- /// JavaScript code version of your plugin.
- ///
- /// Polkawallet App will perform hot-update for the js code
- /// of your plugin with it.
- final int? jsCodeVersion;
- /// Your plugin is connected to a para-chain testNet by default.
- final bool isTestNet;
- /// Whether this para-chain receives assets from relay-chain.
- /// should set [parachainId] if [isXCMSupport] enabled.
- final bool isXCMSupport;
- final String? parachainId;
- }
|