From 46a65b7368148b14e2c3a9bdad36e17a9796c0ad Mon Sep 17 00:00:00 2001 From: shawn Date: Thu, 26 Aug 2021 15:25:29 +0800 Subject: [PATCH] add homaLite tx history pages --- lib/api/types/txHomaData.dart | 26 +++++-- lib/common/constants/base.dart | 4 +- lib/common/constants/nodeList.dart | 64 ++++++++-------- lib/common/constants/subQuery.dart | 29 ++------ lib/pages/homa/homaHistoryPage.dart | 106 +++++++++++++++++++-------- lib/pages/homa/homaPage.dart | 3 +- lib/pages/homa/homaTxDetailPage.dart | 65 ++++++++++++++++ lib/polkawallet_plugin_acala.dart | 9 ++- lib/service/serviceAssets.dart | 12 --- lib/service/serviceLoan.dart | 35 +++++---- 10 files changed, 226 insertions(+), 127 deletions(-) create mode 100644 lib/pages/homa/homaTxDetailPage.dart diff --git a/lib/api/types/txHomaData.dart b/lib/api/types/txHomaData.dart index a1c78b3f..b9c41e65 100644 --- a/lib/api/types/txHomaData.dart +++ b/lib/api/types/txHomaData.dart @@ -1,3 +1,5 @@ +import 'package:polkawallet_ui/utils/format.dart'; + class TxHomaData extends _TxHomaData { static const String actionMint = 'mint'; static const String actionRedeem = 'redeem'; @@ -8,19 +10,27 @@ class TxHomaData extends _TxHomaData { static const String redeemTypeWait = 'WaitForUnbonding'; static TxHomaData fromJson(Map json) { TxHomaData data = TxHomaData(); - data.hash = json['hash']; - data.action = json['action']; - data.amountPay = json['amountPay']; - data.amountReceive = json['amountReceive']; - data.time = DateTime.fromMillisecondsSinceEpoch(json['time']); + data.action = json['extrinsic']['method']; + data.hash = json['extrinsic']['id']; + + data.action = json['extrinsic']['method']; + data.amountPay = Fmt.balanceInt(json['data'][1]['value'].toString()); + data.amountReceive = Fmt.balanceInt(json['data'][2]['value'].toString()); + + data.time = (json['extrinsic']['timestamp'] as String).replaceAll(' ', ''); + data.isSuccess = json['extrinsic']['isSuccess']; return data; } } abstract class _TxHomaData { + String block; String hash; + String action; - String amountPay; - String amountReceive; - DateTime time; + BigInt amountPay; + BigInt amountReceive; + + String time; + bool isSuccess = true; } diff --git a/lib/common/constants/base.dart b/lib/common/constants/base.dart index 1da7db4f..762ca34c 100644 --- a/lib/common/constants/base.dart +++ b/lib/common/constants/base.dart @@ -9,6 +9,6 @@ const ss58_prefix_karura = 8; const GraphQLConfig = { plugin_name_acala: {'httpUri': 'https://api.polkawallet.io/acala-subql'}, - plugin_name_karura: {'httpUri': 'https://api.polkawallet.io/karura-subql'}, - // plugin_name_karura: {'httpUri': 'http://47.92.117.129/'}, + plugin_name_karura: {'httpUri': 'https://api.polkawallet.io/acala-subql'}, + // plugin_name_karura: {'httpUri': 'http://47.243.180.213/'}, }; diff --git a/lib/common/constants/nodeList.dart b/lib/common/constants/nodeList.dart index 34bf6e09..51cd0f4f 100644 --- a/lib/common/constants/nodeList.dart +++ b/lib/common/constants/nodeList.dart @@ -14,40 +14,40 @@ const node_list = { }, ], plugin_name_karura: [ - // { - // 'name': 'Karura (Hosted by Polkawallet)', - // 'ss58': ss58_prefix_karura, - // 'endpoint': 'wss://karura.polkawallet.io', - // }, - // { - // 'name': 'Karura (Hosted by Acala Foundation 0)', - // 'ss58': ss58_prefix_karura, - // 'endpoint': 'wss://karura-rpc-0.aca-api.network', - // }, - // { - // 'name': 'Karura (Hosted by Acala Foundation 1)', - // 'ss58': ss58_prefix_karura, - // 'endpoint': 'wss://karura-rpc-1.aca-api.network', - // }, - // { - // 'name': 'Karura (Hosted by Acala Foundation 2)', - // 'ss58': ss58_prefix_karura, - // 'endpoint': 'wss://karura-rpc-2.aca-api.network', - // }, - // { - // 'name': 'Karura (Hosted by Acala Foundation 3)', - // 'ss58': ss58_prefix_karura, - // 'endpoint': 'wss://karura-rpc-3.aca-api.network', - // }, - // { - // 'name': 'Karura (Hosted by OnFinality)', - // 'ss58': ss58_prefix_karura, - // 'endpoint': 'wss://karura.api.onfinality.io', - // }, { - 'name': 'Acala Karura (Polkawallet dev node)', + 'name': 'Karura (Hosted by Polkawallet)', 'ss58': ss58_prefix_karura, - 'endpoint': 'wss://kusama-1.polkawallet.io:9944', + 'endpoint': 'wss://karura.polkawallet.io', }, + { + 'name': 'Karura (Hosted by Acala Foundation 0)', + 'ss58': ss58_prefix_karura, + 'endpoint': 'wss://karura-rpc-0.aca-api.network', + }, + { + 'name': 'Karura (Hosted by Acala Foundation 1)', + 'ss58': ss58_prefix_karura, + 'endpoint': 'wss://karura-rpc-1.aca-api.network', + }, + { + 'name': 'Karura (Hosted by Acala Foundation 2)', + 'ss58': ss58_prefix_karura, + 'endpoint': 'wss://karura-rpc-2.aca-api.network', + }, + { + 'name': 'Karura (Hosted by Acala Foundation 3)', + 'ss58': ss58_prefix_karura, + 'endpoint': 'wss://karura-rpc-3.aca-api.network', + }, + { + 'name': 'Karura (Hosted by OnFinality)', + 'ss58': ss58_prefix_karura, + 'endpoint': 'wss://karura.api.onfinality.io', + }, + // { + // 'name': 'Acala Karura (Polkawallet dev node)', + // 'ss58': ss58_prefix_karura, + // 'endpoint': 'wss://kusama-1.polkawallet.io:9944', + // }, ], }; diff --git a/lib/common/constants/subQuery.dart b/lib/common/constants/subQuery.dart index 5d58ec26..04f3da2b 100644 --- a/lib/common/constants/subQuery.dart +++ b/lib/common/constants/subQuery.dart @@ -79,34 +79,17 @@ const dexStakeQuery = r''' '''; const homaQuery = r''' query ($account: String) { - calls(filter: { - and: [ - { - or: [ - { method: {equalTo: "mint"} }, - { method: {equalTo: "redeem"} } - ] - }, - { section: {equalTo: "homa"} }, - { signerId: { equalTo: $account } } - ] - }, orderBy: TIMESTAMP_DESC, first: 20) { + homaActions(filter: {accountId: {equalTo: $account}}, + orderBy: TIMESTAMP_DESC, first: 20) { nodes { id - method - section - args - isSuccess + type + data extrinsic { id - block {number} + method timestamp - events { - nodes { - data, - method - } - } + isSuccess } } } diff --git a/lib/pages/homa/homaHistoryPage.dart b/lib/pages/homa/homaHistoryPage.dart index 24d74886..6baefa02 100644 --- a/lib/pages/homa/homaHistoryPage.dart +++ b/lib/pages/homa/homaHistoryPage.dart @@ -1,8 +1,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:intl/intl.dart'; import 'package:polkawallet_plugin_acala/api/types/txHomaData.dart'; import 'package:polkawallet_plugin_acala/common/constants/index.dart'; +import 'package:polkawallet_plugin_acala/common/constants/subQuery.dart'; +import 'package:polkawallet_plugin_acala/pages/homa/homaTxDetailPage.dart'; import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; import 'package:polkawallet_plugin_acala/utils/i18n/index.dart'; import 'package:polkawallet_sdk/storage/keyring.dart'; @@ -19,8 +23,9 @@ class HomaHistoryPage extends StatelessWidget { @override Widget build(BuildContext context) { + final symbols = plugin.networkState.tokenSymbol; + final decimals = plugin.networkState.tokenDecimals; final symbol = relay_chain_token_symbol[plugin.basic.name]; - final list = plugin.store.homa.txs.reversed.toList(); return Scaffold( appBar: AppBar( title: Text( @@ -28,40 +33,77 @@ class HomaHistoryPage extends StatelessWidget { centerTitle: true, ), body: SafeArea( - child: ListView.builder( - itemCount: list.length + 1, - itemBuilder: (BuildContext context, int i) { - if (i == list.length) { - return ListTail(isEmpty: list.length == 0, isLoading: false); + child: Query( + options: QueryOptions( + document: gql(homaQuery), + variables: { + 'account': keyring.current.address, + }, + ), + builder: ( + QueryResult result, { + Future Function() refetch, + FetchMore fetchMore, + }) { + if (result.data == null) { + return Container( + height: MediaQuery.of(context).size.height / 3, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [CupertinoActivityIndicator()], + ), + ); } - final detail = list[i]; + final list = List.of(result.data['homaActions']['nodes']) + .map((i) => TxHomaData.fromJson(i as Map)) + .toList(); - String amountPay = detail.amountPay ?? '0'; - String amountReceive = detail.amountReceive ?? '0'; - if (detail.action == TxHomaData.actionRedeem) { - amountPay += ' L$symbol'; - amountReceive += ' $symbol'; - } else { - amountPay += ' $symbol'; - amountReceive += ' L$symbol'; - } - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide(width: 0.5, color: Colors.black12)), - ), - child: ListTile( - title: Text('${list[i].action} $amountReceive'), - subtitle: Text(Fmt.dateTime(list[i].time)), - leading: - SvgPicture.asset('assets/images/assets_up.svg', width: 32), - trailing: Text( - amountPay, - style: Theme.of(context).textTheme.headline4, - textAlign: TextAlign.end, - ), - ), + final nativeDecimal = decimals[symbols.indexOf(symbol)]; + final liquidDecimal = decimals[symbols.indexOf('L$symbol')]; + + return ListView.builder( + itemCount: list.length + 1, + itemBuilder: (BuildContext context, int i) { + if (i == list.length) { + return ListTail(isEmpty: list.length == 0, isLoading: false); + } + + final detail = list[i]; + + String amountPay = + Fmt.priceFloorBigInt(detail.amountPay, nativeDecimal); + String amountReceive = + Fmt.priceFloorBigInt(detail.amountReceive, liquidDecimal); + if (detail.action == TxHomaData.actionRedeem) { + amountPay += ' L$symbol'; + amountReceive += ' $symbol'; + } else { + amountPay += ' $symbol'; + amountReceive += ' L$symbol'; + } + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(width: 0.5, color: Colors.black12)), + ), + child: ListTile( + title: Text('${detail.action} $amountReceive'), + subtitle: Text(Fmt.dateTime( + DateFormat("yyyy-MM-ddTHH:mm:ss") + .parse(detail.time, true))), + leading: SvgPicture.asset('assets/images/assets_up.svg', + width: 32), + trailing: Text( + amountPay, + style: Theme.of(context).textTheme.headline4, + textAlign: TextAlign.end, + ), + onTap: () => Navigator.of(context) + .pushNamed(HomaTxDetailPage.route, arguments: detail), + ), + ); + }, ); }, ), diff --git a/lib/pages/homa/homaPage.dart b/lib/pages/homa/homaPage.dart index 91b1b7bf..81e90107 100644 --- a/lib/pages/homa/homaPage.dart +++ b/lib/pages/homa/homaPage.dart @@ -152,6 +152,7 @@ class _HomaPageState extends State { final poolInfo = widget.plugin.store.homa.poolInfo; final staked = poolInfo.staked ?? BigInt.zero; final cap = poolInfo.cap ?? BigInt.zero; + final amountLeft = cap - staked; final liquidTokenIssuance = poolInfo.liquidTokenIssuance ?? BigInt.zero; final List seriesList = [ @@ -164,7 +165,7 @@ class _HomaPageState extends State { measureFn: (num i, _) => i, data: [ staked.toDouble(), - cap.toDouble(), + amountLeft.toDouble(), ], ) ]; diff --git a/lib/pages/homa/homaTxDetailPage.dart b/lib/pages/homa/homaTxDetailPage.dart new file mode 100644 index 00000000..b66d5ebb --- /dev/null +++ b/lib/pages/homa/homaTxDetailPage.dart @@ -0,0 +1,65 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:polkawallet_plugin_acala/api/types/txHomaData.dart'; +import 'package:polkawallet_plugin_acala/common/constants/index.dart'; +import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; +import 'package:polkawallet_plugin_acala/utils/i18n/index.dart'; +import 'package:polkawallet_sdk/storage/keyring.dart'; +import 'package:polkawallet_sdk/utils/i18n.dart'; +import 'package:polkawallet_ui/components/txDetail.dart'; +import 'package:polkawallet_ui/utils/format.dart'; + +class HomaTxDetailPage extends StatelessWidget { + HomaTxDetailPage(this.plugin, this.keyring); + final PluginAcala plugin; + final Keyring keyring; + + static final String route = '/acala/homa/tx'; + + @override + Widget build(BuildContext context) { + final Map dic = + I18n.of(context).getDic(i18n_full_dic_acala, 'acala'); + final decimals = plugin.networkState.tokenDecimals; + final symbols = plugin.networkState.tokenSymbol; + + final TxHomaData tx = ModalRoute.of(context).settings.arguments; + + final symbol = relay_chain_token_symbol[plugin.basic.name]; + final nativeDecimal = decimals[symbols.indexOf(symbol)]; + final liquidDecimal = decimals[symbols.indexOf('L$symbol')]; + + String amountPay = Fmt.priceFloorBigInt(tx.amountPay, nativeDecimal); + String amountReceive = + Fmt.priceFloorBigInt(tx.amountReceive, liquidDecimal); + if (tx.action == TxHomaData.actionRedeem) { + amountPay += ' L$symbol'; + amountReceive += ' $symbol'; + } else { + amountPay += ' $symbol'; + amountReceive += ' L$symbol'; + } + + final amountStyle = TextStyle(fontSize: 16, fontWeight: FontWeight.bold); + return TxDetail( + success: tx.isSuccess, + action: tx.action, + // blockNum: int.parse(tx.block), + hash: tx.hash, + blockTime: + Fmt.dateTime(DateFormat("yyyy-MM-ddTHH:mm:ss").parse(tx.time, true)), + networkName: plugin.basic.name, + infoItems: [ + TxDetailInfoItem( + label: dic['dex.pay'], + content: Text(amountPay, style: amountStyle), + ), + TxDetailInfoItem( + label: dic['dex.receive'], + content: Text(amountReceive, style: amountStyle), + ) + ], + ); + } +} diff --git a/lib/polkawallet_plugin_acala.dart b/lib/polkawallet_plugin_acala.dart index 39a9e405..0844c606 100644 --- a/lib/polkawallet_plugin_acala.dart +++ b/lib/polkawallet_plugin_acala.dart @@ -30,6 +30,7 @@ import 'package:polkawallet_plugin_acala/pages/gov/democracy/referendumVotePage. import 'package:polkawallet_plugin_acala/pages/gov/democracyPage.dart'; import 'package:polkawallet_plugin_acala/pages/homa/homaHistoryPage.dart'; import 'package:polkawallet_plugin_acala/pages/homa/homaPage.dart'; +import 'package:polkawallet_plugin_acala/pages/homa/homaTxDetailPage.dart'; import 'package:polkawallet_plugin_acala/pages/homa/mintPage.dart'; import 'package:polkawallet_plugin_acala/pages/homa/redeemPage.dart'; import 'package:polkawallet_plugin_acala/pages/loan/loanAdjustPage.dart'; @@ -200,7 +201,13 @@ class PluginAcala extends PolkawalletPlugin { HomaPage.route: (_) => HomaPage(this, keyring), MintPage.route: (_) => MintPage(this, keyring), HomaRedeemPage.route: (_) => HomaRedeemPage(this, keyring), - HomaHistoryPage.route: (_) => HomaHistoryPage(this, keyring), + HomaHistoryPage.route: (_) => ClientProvider( + child: Builder( + builder: (_) => HomaHistoryPage(this, keyring), + ), + uri: GraphQLConfig[_service.plugin.basic.name]['httpUri'], + ), + HomaTxDetailPage.route: (_) => HomaTxDetailPage(this, keyring), // NFT pages NFTPage.route: (_) => NFTPage(this, keyring), // Gov pages diff --git a/lib/service/serviceAssets.dart b/lib/service/serviceAssets.dart index d8c0c7e0..bab24aaf 100644 --- a/lib/service/serviceAssets.dart +++ b/lib/service/serviceAssets.dart @@ -33,18 +33,6 @@ class ServiceAssets { } }); - // todo: remove this after homaToken enabled - final relayChainToken = relay_chain_token_symbol[plugin.basic.name]; - final homaToken = 'L$relayChainToken'; - if (store.assets.marketPrices[homaToken] == null && - prices[homaToken] == null) { - if (store.assets.marketPrices[relayChainToken] != null || - prices[relayChainToken] != null) { - prices[homaToken] = store.assets.marketPrices[relayChainToken] ?? - prices[relayChainToken]; - } - } - store.assets.setMarketPrices(prices); } diff --git a/lib/service/serviceLoan.dart b/lib/service/serviceLoan.dart index 2e021f99..20b4de38 100644 --- a/lib/service/serviceLoan.dart +++ b/lib/service/serviceLoan.dart @@ -1,5 +1,6 @@ import 'package:polkawallet_plugin_acala/api/acalaApi.dart'; import 'package:polkawallet_plugin_acala/api/types/loanType.dart'; +import 'package:polkawallet_plugin_acala/api/types/stakingPoolInfoData.dart'; import 'package:polkawallet_plugin_acala/common/constants/base.dart'; import 'package:polkawallet_plugin_acala/common/constants/index.dart'; import 'package:polkawallet_plugin_acala/polkawallet_plugin_acala.dart'; @@ -18,13 +19,17 @@ class ServiceLoan { final PluginStore store; void _calcLiquidTokenPrice( - Map prices, double liquidExchangeRate) { + Map prices, HomaLitePoolInfoData poolInfo) { // LDOT price may lost precision here - final nativeToken = plugin.networkState.tokenSymbol[0]; - prices['L$nativeToken'] = Fmt.tokenInt( + final relayToken = relay_chain_token_symbol[plugin.basic.name]; + final exchangeRate = poolInfo.staked > BigInt.zero + ? (poolInfo.liquidTokenIssuance / poolInfo.staked) + : Fmt.balanceInt( + plugin.networkConst['homaLite']['defaultExchangeRate']); + prices['L$relayToken'] = Fmt.tokenInt( (Fmt.bigIntToDouble( - prices[nativeToken], plugin.networkState.tokenDecimals[0]) * - liquidExchangeRate) + prices[relayToken], plugin.networkState.tokenDecimals[0]) * + exchangeRate) .toString(), plugin.networkState.tokenDecimals[0]); } @@ -103,17 +108,15 @@ class ServiceLoan { // 1. subscribe all token prices, callback triggers per 5s. api.assets.subscribeTokenPrices((Map prices) async { - final modulesConfig = store.setting.liveModules; - if (modulesConfig['homa'] != null && modulesConfig['homa']['enabled']) { - // 2. we need homa staking pool info to calculate price of LDOT - final stakingPoolInfo = await api.homa.queryHomaStakingPool(); - store.homa.setStakingPoolInfoData(stakingPoolInfo); - - // 3. set prices - _calcLiquidTokenPrice(prices, stakingPoolInfo.liquidExchangeRate); - // we may not need ACA/KAR prices - // prices['ACA'] = Fmt.tokenInt(data[1].toString(), acala_price_decimals); - } + // 2. we need homa staking pool info to calculate price of LDOT + final stakingPoolInfo = await api.homa.queryHomaLiteStakingPool(); + store.homa.setHomaLitePoolInfoData(stakingPoolInfo); + + // 3. set prices + _calcLiquidTokenPrice(prices, stakingPoolInfo); + // we may not need ACA/KAR prices + // prices['ACA'] = Fmt.tokenInt(data[1].toString(), acala_price_decimals); + store.assets.setPrices(prices); // 4. update collateral incentive rewards