mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-22 15:19:11 +00:00
WIP load and display token transactions
This commit is contained in:
parent
67fbb6ec5e
commit
9c8fd22bfb
6 changed files with 170 additions and 165 deletions
|
@ -4,8 +4,10 @@ import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/models/ethereum/eth_token.dart';
|
import 'package:stackwallet/models/ethereum/eth_token.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||||
|
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
|
||||||
import 'package:stackwallet/services/coins/manager.dart';
|
import 'package:stackwallet/services/coins/manager.dart';
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
||||||
|
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
@ -49,12 +51,12 @@ class MyTokenSelectItem extends ConsumerWidget {
|
||||||
BorderRadius.circular(Constants.size.circularBorderRadius),
|
BorderRadius.circular(Constants.size.circularBorderRadius),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final mnemonicList = ref.read(managerProvider).mnemonic;
|
|
||||||
|
|
||||||
final tokenService = EthereumTokenService(
|
final tokenService = EthereumTokenService(
|
||||||
token: token,
|
token: token,
|
||||||
walletMnemonic: mnemonicList,
|
|
||||||
secureStore: ref.read(secureStoreProvider),
|
secureStore: ref.read(secureStoreProvider),
|
||||||
|
ethWallet: ref.read(managerProvider).wallet as EthereumWallet,
|
||||||
|
tracker: TransactionNotificationTracker(
|
||||||
|
walletId: ref.read(managerProvider).walletId),
|
||||||
);
|
);
|
||||||
|
|
||||||
await showLoading<void>(
|
await showLoading<void>(
|
||||||
|
|
|
@ -208,7 +208,7 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
||||||
.select((value) => value.getManager(widget.walletId)));
|
.select((value) => value.getManager(widget.walletId)));
|
||||||
|
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: widget.tokenService.transaction,
|
future: widget.tokenService.transactions,
|
||||||
builder: (fbContext, AsyncSnapshot<List<Transaction>> snapshot) {
|
builder: (fbContext, AsyncSnapshot<List<Transaction>> snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
snapshot.hasData) {
|
snapshot.hasData) {
|
||||||
|
@ -237,13 +237,8 @@ class _TransactionsListState extends ConsumerState<TokenTransactionsList> {
|
||||||
_transactions2.sort((a, b) => b.timestamp - a.timestamp);
|
_transactions2.sort((a, b) => b.timestamp - a.timestamp);
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
//todo: check if print needed
|
if (!widget.tokenService.isRefreshing) {
|
||||||
// debugPrint("pulled down to refresh on transaction list");
|
unawaited(widget.tokenService.refresh());
|
||||||
final managerProvider = ref
|
|
||||||
.read(walletsChangeNotifierProvider)
|
|
||||||
.getManagerProvider(widget.walletId);
|
|
||||||
if (!ref.read(managerProvider).isRefreshing) {
|
|
||||||
unawaited(ref.read(managerProvider).refresh());
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Util.isDesktop
|
child: Util.isDesktop
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/models/ethereum/eth_token.dart';
|
import 'package:stackwallet/models/ethereum/eth_token.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart';
|
import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
|
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
import 'package:stackwallet/services/ethereum/ethereum_token_service.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
@ -166,9 +166,9 @@ class _TokenViewState extends ConsumerState<TokenView> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TransactionsList(
|
child: TokenTransactionsList(
|
||||||
managerProvider: managerProvider,
|
tokenService: widget.tokenService,
|
||||||
walletId: walletId,
|
walletId: widget.walletId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -130,8 +130,12 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
Future<List<UTXO>> get utxos => db.getUTXOs(walletId).findAll();
|
Future<List<UTXO>> get utxos => db.getUTXOs(walletId).findAll();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Transaction>> get transactions =>
|
Future<List<Transaction>> get transactions => db
|
||||||
db.getTransactions(walletId).sortByTimestampDesc().findAll();
|
.getTransactions(walletId)
|
||||||
|
.filter()
|
||||||
|
.otherDataEqualTo(null)
|
||||||
|
.sortByTimestampDesc()
|
||||||
|
.findAll();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> get currentReceivingAddress async {
|
Future<String> get currentReceivingAddress async {
|
||||||
|
@ -143,7 +147,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
Future<Address?> get _currentReceivingAddress => db
|
Future<Address?> get _currentReceivingAddress => db
|
||||||
.getAddresses(walletId)
|
.getAddresses(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
.typeEqualTo(AddressType.p2wpkh)
|
.typeEqualTo(AddressType.ethereum)
|
||||||
.subTypeEqualTo(AddressSubType.receiving)
|
.subTypeEqualTo(AddressSubType.receiving)
|
||||||
.sortByDerivationIndexDesc()
|
.sortByDerivationIndexDesc()
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
@ -759,8 +763,9 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Caught exception in refreshWalletData(): $error\n$strace",
|
"Caught exception in $walletName $walletId refresh(): $error\n$strace",
|
||||||
level: LogLevel.Warning);
|
level: LogLevel.Warning,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,7 +915,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Failed to refresh transactions for ${coin.prettyName} $walletName $walletId: $txs",
|
"Failed to refresh transactions for ${coin.prettyName} $walletName $walletId",
|
||||||
level: LogLevel.Warning,
|
level: LogLevel.Warning,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,14 +144,18 @@ abstract class EthereumAPI {
|
||||||
|
|
||||||
static Future<EthereumResponse<List<EthTokenTx>>> getTokenTransactions({
|
static Future<EthereumResponse<List<EthTokenTx>>> getTokenTransactions({
|
||||||
required String address,
|
required String address,
|
||||||
|
String? contractAddress,
|
||||||
int? startBlock,
|
int? startBlock,
|
||||||
int? endBlock,
|
int? endBlock,
|
||||||
// todo add more params?
|
// todo add more params?
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final uri = Uri.parse(
|
String uriString =
|
||||||
"$blockExplorer?module=account&action=tokentx&address=$address",
|
"$blockExplorer?module=account&action=tokentx&address=$address";
|
||||||
);
|
if (contractAddress != null) {
|
||||||
|
uriString += "&contractAddress=$contractAddress";
|
||||||
|
}
|
||||||
|
final uri = Uri.parse(uriString);
|
||||||
final response = await get(uri);
|
final response = await get(uri);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
import 'dart:math';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:ethereum_addresses/ethereum_addresses.dart';
|
import 'package:ethereum_addresses/ethereum_addresses.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
import 'package:stackwallet/models/ethereum/eth_token.dart';
|
import 'package:stackwallet/models/ethereum/eth_token.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||||
import 'package:stackwallet/models/node_model.dart';
|
import 'package:stackwallet/models/node_model.dart';
|
||||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
import 'package:stackwallet/models/paymint/transactions_model.dart';
|
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
|
||||||
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
|
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
|
||||||
|
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||||
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
import 'package:stackwallet/services/node_service.dart';
|
import 'package:stackwallet/services/node_service.dart';
|
||||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
import 'package:stackwallet/utilities/default_nodes.dart';
|
||||||
|
@ -16,47 +21,33 @@ import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/eth_commons.dart';
|
import 'package:stackwallet/utilities/eth_commons.dart';
|
||||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:web3dart/web3dart.dart' as web3dart;
|
import 'package:web3dart/web3dart.dart' as web3dart;
|
||||||
|
|
||||||
const int MINIMUM_CONFIRMATIONS = 3;
|
|
||||||
|
|
||||||
class EthereumTokenService {
|
class EthereumTokenService {
|
||||||
final EthToken token;
|
final EthToken token;
|
||||||
|
final EthereumWallet ethWallet;
|
||||||
|
final TransactionNotificationTracker tracker;
|
||||||
|
final SecureStorageInterface _secureStore;
|
||||||
|
|
||||||
late bool shouldAutoSync;
|
|
||||||
late web3dart.EthereumAddress _contractAddress;
|
late web3dart.EthereumAddress _contractAddress;
|
||||||
late web3dart.EthPrivateKey _credentials;
|
late web3dart.EthPrivateKey _credentials;
|
||||||
late web3dart.DeployedContract _contract;
|
late web3dart.DeployedContract _contract;
|
||||||
late web3dart.ContractFunction _balanceFunction;
|
late web3dart.ContractFunction _balanceFunction;
|
||||||
late web3dart.ContractFunction _sendFunction;
|
late web3dart.ContractFunction _sendFunction;
|
||||||
late Future<List<String>> _walletMnemonic;
|
|
||||||
late SecureStorageInterface _secureStore;
|
|
||||||
late String _tokenAbi;
|
late String _tokenAbi;
|
||||||
late web3dart.Web3Client _client;
|
late web3dart.Web3Client _client;
|
||||||
late final TransactionNotificationTracker txTracker;
|
|
||||||
TransactionData? cachedTxData;
|
|
||||||
|
|
||||||
final _gasLimit = 200000;
|
static const _gasLimit = 200000;
|
||||||
|
|
||||||
EthereumTokenService({
|
EthereumTokenService({
|
||||||
required this.token,
|
required this.token,
|
||||||
required Future<List<String>> walletMnemonic,
|
required this.ethWallet,
|
||||||
required SecureStorageInterface secureStore,
|
required SecureStorageInterface secureStore,
|
||||||
}) {
|
required this.tracker,
|
||||||
|
}) : _secureStore = secureStore {
|
||||||
_contractAddress = web3dart.EthereumAddress.fromHex(token.contractAddress);
|
_contractAddress = web3dart.EthereumAddress.fromHex(token.contractAddress);
|
||||||
_walletMnemonic = walletMnemonic;
|
|
||||||
_secureStore = secureStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<String>> get allOwnAddresses =>
|
|
||||||
_allOwnAddresses ??= _fetchAllOwnAddresses();
|
|
||||||
Future<List<String>>? _allOwnAddresses;
|
|
||||||
|
|
||||||
Future<List<String>> _fetchAllOwnAddresses() async {
|
|
||||||
List<String> addresses = [];
|
|
||||||
final ownAddress = _credentials.address;
|
|
||||||
addresses.add(ownAddress.toString());
|
|
||||||
return addresses;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Decimal> get availableBalance async {
|
Future<Decimal> get availableBalance async {
|
||||||
|
@ -89,12 +80,19 @@ class EthereumTokenService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> get currentReceivingAddress async {
|
Future<String> get currentReceivingAddress async {
|
||||||
final _currentReceivingAddress = await _credentials.extractAddress();
|
final address = await _currentReceivingAddress;
|
||||||
final checkSumAddress =
|
return address?.value ??
|
||||||
checksumEthereumAddress(_currentReceivingAddress.toString());
|
checksumEthereumAddress(_credentials.address.toString());
|
||||||
return checkSumAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Address?> get _currentReceivingAddress => ethWallet.db
|
||||||
|
.getAddresses(ethWallet.walletId)
|
||||||
|
.filter()
|
||||||
|
.typeEqualTo(AddressType.ethereum)
|
||||||
|
.subTypeEqualTo(AddressSubType.receiving)
|
||||||
|
.sortByDerivationIndexDesc()
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
|
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
|
||||||
final fee = estimateFee(feeRate, _gasLimit, token.decimals);
|
final fee = estimateFee(feeRate, _gasLimit, token.decimals);
|
||||||
return Format.decimalAmountToSatoshis(Decimal.parse(fee.toString()), coin);
|
return Format.decimalAmountToSatoshis(Decimal.parse(fee.toString()), coin);
|
||||||
|
@ -111,12 +109,11 @@ class EthereumTokenService {
|
||||||
_tokenAbi = (await _secureStore.read(
|
_tokenAbi = (await _secureStore.read(
|
||||||
key: '${_contractAddress.toString()}_tokenAbi'))!;
|
key: '${_contractAddress.toString()}_tokenAbi'))!;
|
||||||
|
|
||||||
final mnemonic = await _walletMnemonic;
|
String? mnemonicString = await ethWallet.mnemonicString;
|
||||||
String mnemonicString = mnemonic.join(' ');
|
|
||||||
|
|
||||||
//Get private key for given mnemonic
|
//Get private key for given mnemonic
|
||||||
// TODO: replace empty string with actual passphrase
|
String privateKey = getPrivateKey(
|
||||||
String privateKey = getPrivateKey(mnemonicString, "");
|
mnemonicString!, (await ethWallet.mnemonicPassphrase) ?? "");
|
||||||
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
||||||
|
|
||||||
_contract = web3dart.DeployedContract(
|
_contract = web3dart.DeployedContract(
|
||||||
|
@ -125,7 +122,7 @@ class EthereumTokenService {
|
||||||
_sendFunction = _contract.function('transfer');
|
_sendFunction = _contract.function('transfer');
|
||||||
_client = await getEthClient();
|
_client = await getEthClient();
|
||||||
|
|
||||||
// print(_credentials.p)
|
unawaited(refresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initializeNew() async {
|
Future<void> initializeNew() async {
|
||||||
|
@ -141,12 +138,11 @@ class EthereumTokenService {
|
||||||
throw Exception('Failed to load token abi');
|
throw Exception('Failed to load token abi');
|
||||||
}
|
}
|
||||||
|
|
||||||
final mnemonic = await _walletMnemonic;
|
String? mnemonicString = await ethWallet.mnemonicString;
|
||||||
String mnemonicString = mnemonic.join(' ');
|
|
||||||
|
|
||||||
//Get private key for given mnemonic
|
//Get private key for given mnemonic
|
||||||
// TODO: replace empty string with actual passphrase
|
String privateKey = getPrivateKey(
|
||||||
String privateKey = getPrivateKey(mnemonicString, "");
|
mnemonicString!, (await ethWallet.mnemonicPassphrase) ?? "");
|
||||||
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
||||||
|
|
||||||
_contract = web3dart.DeployedContract(
|
_contract = web3dart.DeployedContract(
|
||||||
|
@ -154,16 +150,11 @@ class EthereumTokenService {
|
||||||
_balanceFunction = _contract.function('balanceOf');
|
_balanceFunction = _contract.function('balanceOf');
|
||||||
_sendFunction = _contract.function('transfer');
|
_sendFunction = _contract.function('transfer');
|
||||||
_client = await getEthClient();
|
_client = await getEthClient();
|
||||||
|
|
||||||
|
unawaited(refresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement isRefreshing
|
bool get isRefreshing => _refreshLock;
|
||||||
bool get isRefreshing => throw UnimplementedError();
|
|
||||||
|
|
||||||
Future<int> get maxFee async {
|
|
||||||
final fee = (await fees).fast;
|
|
||||||
final feeEstimate = await estimateFeeFor(0, fee);
|
|
||||||
return feeEstimate;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> prepareSend(
|
Future<Map<String, dynamic>> prepareSend(
|
||||||
{required String address,
|
{required String address,
|
||||||
|
@ -208,9 +199,22 @@ class EthereumTokenService {
|
||||||
return txData;
|
return txData;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> refresh() {
|
bool _refreshLock = false;
|
||||||
// TODO: implement refresh
|
|
||||||
throw UnimplementedError();
|
Future<void> refresh() async {
|
||||||
|
if (!_refreshLock) {
|
||||||
|
_refreshLock = true;
|
||||||
|
try {
|
||||||
|
await _refreshTransactions();
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Caught exception in ${token.name} ${ethWallet.walletName} ${ethWallet.walletId} refresh(): $e\n$s",
|
||||||
|
level: LogLevel.Warning,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
_refreshLock = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Decimal> get totalBalance async {
|
Future<Decimal> get totalBalance async {
|
||||||
|
@ -227,108 +231,103 @@ class EthereumTokenService {
|
||||||
return Decimal.parse(balanceInDecimal.toString());
|
return Decimal.parse(balanceInDecimal.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TransactionData> get transactionData =>
|
Future<List<Transaction>> get transactions => ethWallet.db
|
||||||
_transactionData ??= _fetchTransactionData();
|
.getTransactions(ethWallet.walletId)
|
||||||
Future<TransactionData>? _transactionData;
|
.filter()
|
||||||
|
.otherDataEqualTo(token.contractAddress)
|
||||||
|
.sortByTimestampDesc()
|
||||||
|
.findAll();
|
||||||
|
|
||||||
Future<TransactionData> _fetchTransactionData() async {
|
Future<void> _refreshTransactions() async {
|
||||||
String thisAddress = await currentReceivingAddress;
|
String addressString = await currentReceivingAddress;
|
||||||
|
|
||||||
final List<Map<String, dynamic>> midSortedArray = [];
|
final response = await EthereumAPI.getTokenTransactions(
|
||||||
|
address: addressString,
|
||||||
|
contractAddress: token.contractAddress,
|
||||||
|
);
|
||||||
|
|
||||||
AddressTransaction txs =
|
if (response.value == null) {
|
||||||
await EthereumAPI.fetchAddressTransactions(thisAddress, "tokentx");
|
throw Exception("Failed to fetch token transactions");
|
||||||
|
|
||||||
if (txs.message == "OK") {
|
|
||||||
final allTxs = txs.result;
|
|
||||||
for (var element in allTxs) {
|
|
||||||
Map<String, dynamic> midSortedTx = {};
|
|
||||||
// create final tx map
|
|
||||||
midSortedTx["txid"] = element["hash"];
|
|
||||||
int confirmations = int.parse(element['confirmations'].toString());
|
|
||||||
|
|
||||||
int transactionAmount = int.parse(element['value'].toString());
|
|
||||||
int decimal = token.decimals; //Eth has up to 18 decimal places
|
|
||||||
final transactionAmountInDecimal =
|
|
||||||
transactionAmount / (pow(10, decimal));
|
|
||||||
|
|
||||||
//Convert to satoshi, default display for other coins
|
|
||||||
final satAmount = Format.decimalAmountToSatoshis(
|
|
||||||
Decimal.parse(transactionAmountInDecimal.toString()), coin);
|
|
||||||
|
|
||||||
midSortedTx["confirmed_status"] =
|
|
||||||
(confirmations != 0) && (confirmations >= MINIMUM_CONFIRMATIONS);
|
|
||||||
midSortedTx["confirmations"] = confirmations;
|
|
||||||
midSortedTx["timestamp"] = element["timeStamp"];
|
|
||||||
|
|
||||||
if (checksumEthereumAddress(element["from"].toString()) ==
|
|
||||||
thisAddress) {
|
|
||||||
midSortedTx["txType"] = "Sent";
|
|
||||||
} else {
|
|
||||||
midSortedTx["txType"] = "Received";
|
|
||||||
}
|
|
||||||
|
|
||||||
midSortedTx["amount"] = satAmount;
|
|
||||||
|
|
||||||
//Calculate fees (GasLimit * gasPrice)
|
|
||||||
int txFee = int.parse(element['gasPrice'].toString()) *
|
|
||||||
int.parse(element['gasUsed'].toString());
|
|
||||||
final txFeeDecimal = txFee / (pow(10, decimal));
|
|
||||||
|
|
||||||
midSortedTx["aliens"] = <dynamic>[];
|
|
||||||
midSortedTx["fees"] = Format.decimalAmountToSatoshis(
|
|
||||||
Decimal.parse(txFeeDecimal.toString()), coin);
|
|
||||||
midSortedTx["address"] = element["to"];
|
|
||||||
midSortedTx["inputSize"] = 1;
|
|
||||||
midSortedTx["outputSize"] = 1;
|
|
||||||
midSortedTx["inputs"] = <dynamic>[];
|
|
||||||
midSortedTx["outputs"] = <dynamic>[];
|
|
||||||
midSortedTx["height"] = int.parse(element['blockNumber'].toString());
|
|
||||||
|
|
||||||
midSortedArray.add(midSortedTx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
midSortedArray.sort((a, b) =>
|
final List<Tuple2<Transaction, Address?>> txnsData = [];
|
||||||
(int.parse(b['timestamp'].toString())) -
|
|
||||||
(int.parse(a['timestamp'].toString())));
|
|
||||||
|
|
||||||
// buildDateTimeChunks
|
for (final tx in response.value!) {
|
||||||
final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
|
bool isIncoming;
|
||||||
final dateArray = <dynamic>[];
|
if (checksumEthereumAddress(tx.from) == addressString) {
|
||||||
|
isIncoming = false;
|
||||||
for (int i = 0; i < midSortedArray.length; i++) {
|
|
||||||
final txObject = midSortedArray[i];
|
|
||||||
final date =
|
|
||||||
extractDateFromTimestamp(int.parse(txObject['timestamp'].toString()));
|
|
||||||
final txTimeArray = [txObject["timestamp"], date];
|
|
||||||
|
|
||||||
if (dateArray.contains(txTimeArray[1])) {
|
|
||||||
result["dateTimeChunks"].forEach((dynamic chunk) {
|
|
||||||
if (extractDateFromTimestamp(
|
|
||||||
int.parse(chunk['timestamp'].toString())) ==
|
|
||||||
txTimeArray[1]) {
|
|
||||||
if (chunk["transactions"] == null) {
|
|
||||||
chunk["transactions"] = <Map<String, dynamic>>[];
|
|
||||||
}
|
|
||||||
chunk["transactions"].add(txObject);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
dateArray.add(txTimeArray[1]);
|
isIncoming = true;
|
||||||
final chunk = {
|
|
||||||
"timestamp": txTimeArray[0],
|
|
||||||
"transactions": [txObject],
|
|
||||||
};
|
|
||||||
result["dateTimeChunks"].add(chunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final txn = Transaction(
|
||||||
|
walletId: ethWallet.walletId,
|
||||||
|
txid: tx.hash,
|
||||||
|
timestamp: tx.timeStamp,
|
||||||
|
type: isIncoming ? TransactionType.incoming : TransactionType.outgoing,
|
||||||
|
subType: TransactionSubType.ethToken,
|
||||||
|
amount: tx.value.toInt(),
|
||||||
|
fee: tx.gasUsed * tx.gasPrice.toInt(),
|
||||||
|
height: tx.blockNumber,
|
||||||
|
isCancelled: false,
|
||||||
|
isLelantus: false,
|
||||||
|
slateId: null,
|
||||||
|
otherData: tx.contractAddress,
|
||||||
|
inputs: [],
|
||||||
|
outputs: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
Address? transactionAddress = await ethWallet.db
|
||||||
|
.getAddresses(ethWallet.walletId)
|
||||||
|
.filter()
|
||||||
|
.valueEqualTo(addressString)
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if (transactionAddress == null) {
|
||||||
|
if (isIncoming) {
|
||||||
|
transactionAddress = Address(
|
||||||
|
walletId: ethWallet.walletId,
|
||||||
|
value: addressString,
|
||||||
|
publicKey: [],
|
||||||
|
derivationIndex: 0,
|
||||||
|
derivationPath: DerivationPath()..value = "$hdPathEthereum/0",
|
||||||
|
type: AddressType.ethereum,
|
||||||
|
subType: AddressSubType.receiving,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final myRcvAddr = await currentReceivingAddress;
|
||||||
|
final isSentToSelf = myRcvAddr == addressString;
|
||||||
|
|
||||||
|
transactionAddress = Address(
|
||||||
|
walletId: ethWallet.walletId,
|
||||||
|
value: addressString,
|
||||||
|
publicKey: [],
|
||||||
|
derivationIndex: isSentToSelf ? 0 : -1,
|
||||||
|
derivationPath: isSentToSelf
|
||||||
|
? (DerivationPath()..value = "$hdPathEthereum/0")
|
||||||
|
: null,
|
||||||
|
type: AddressType.ethereum,
|
||||||
|
subType: isSentToSelf
|
||||||
|
? AddressSubType.receiving
|
||||||
|
: AddressSubType.nonWallet,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txnsData.add(Tuple2(txn, transactionAddress));
|
||||||
}
|
}
|
||||||
|
await ethWallet.db.addNewTransactionData(txnsData, ethWallet.walletId);
|
||||||
|
|
||||||
final txModel = TransactionData.fromMap(
|
// quick hack to notify manager to call notifyListeners if
|
||||||
TransactionData.fromJson(result).getAllTransactions());
|
// transactions changed
|
||||||
|
if (txnsData.isNotEmpty) {
|
||||||
cachedTxData = txModel;
|
GlobalEventBus.instance.fire(
|
||||||
return txModel;
|
UpdatedInBackgroundEvent(
|
||||||
|
"${token.name} transactions updated/added for: ${ethWallet.walletId} ${ethWallet.walletName}",
|
||||||
|
ethWallet.walletId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validateAddress(String address) {
|
bool validateAddress(String address) {
|
||||||
|
|
Loading…
Reference in a new issue