Skip to content

Commit 4d97960

Browse files
committed
feat: listen to epicbox and create temp txs therefrom before synced
1 parent 2f40e79 commit 4d97960

File tree

1 file changed

+161
-2
lines changed

1 file changed

+161
-2
lines changed

lib/wallets/wallet/impl/epiccash_wallet.dart

Lines changed: 161 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,8 @@ class EpiccashWallet extends Bip39Wallet {
396396
}
397397

398398
Logging.instance.d("_startScans successfully at the tip");
399-
//Once scanner completes restart listener
400-
await _listenToEpicbox();
399+
// await _listenToEpicbox();
400+
// Epicbox listener already started before scanning.
401401
} catch (e, s) {
402402
Logging.instance.e("_startScans failed: ", error: e, stackTrace: s);
403403
rethrow;
@@ -775,6 +775,21 @@ class EpiccashWallet extends Bip39Wallet {
775775
await _generateAndStoreReceivingAddressForIndex(curAdd);
776776

777777
if (doScan) {
778+
// Start epicbox listener first for instant transaction appearance.
779+
await _listenToEpicbox();
780+
781+
// Immediately check for epicbox transactions without node dependency.
782+
try {
783+
await _updateTransactionsWithoutNodeRefresh();
784+
await updateBalance(); // Update balance to show pending transactions.
785+
} catch (e, s) {
786+
Logging.instance.w(
787+
"Initial epicbox transaction check failed",
788+
error: e,
789+
stackTrace: s,
790+
);
791+
}
792+
778793
await _startScans();
779794

780795
unawaited(_startSync());
@@ -896,6 +911,150 @@ class EpiccashWallet extends Bip39Wallet {
896911
}
897912
}
898913

914+
/// Updates transactions without refreshing from node (for epicbox-only transactions).
915+
Future<void> _updateTransactionsWithoutNodeRefresh() async {
916+
try {
917+
_hackedCheckTorNodePrefs();
918+
final wallet = await secureStorageInterface.read(
919+
key: '${walletId}_wallet',
920+
);
921+
const refreshFromNode =
922+
0; // Don't refresh from node, use cached/epicbox data.
923+
924+
final myAddresses =
925+
await mainDB
926+
.getAddresses(walletId)
927+
.filter()
928+
.typeEqualTo(AddressType.mimbleWimble)
929+
.and()
930+
.subTypeEqualTo(AddressSubType.receiving)
931+
.and()
932+
.valueIsNotEmpty()
933+
.valueProperty()
934+
.findAll();
935+
final myAddressesSet = myAddresses.toSet();
936+
937+
final transactions = await epiccash.LibEpiccash.getTransactions(
938+
wallet: wallet!,
939+
refreshFromNode: refreshFromNode,
940+
);
941+
942+
final List<TransactionV2> txns = [];
943+
944+
final slatesToCommits = info.epicData?.slatesToCommits ?? {};
945+
946+
for (final tx in transactions) {
947+
final isIncoming =
948+
tx.txType == epic_models.TransactionType.TxReceived ||
949+
tx.txType == epic_models.TransactionType.TxReceivedCancelled;
950+
final slateId = tx.txSlateId;
951+
final commitId = slatesToCommits[slateId]?['commitId'] as String?;
952+
final numberOfMessages = tx.messages?.messages.length;
953+
final onChainNote = tx.messages?.messages[0].message;
954+
final addressFrom = slatesToCommits[slateId]?["from"] as String?;
955+
final addressTo = slatesToCommits[slateId]?["to"] as String?;
956+
957+
final credit = int.parse(tx.amountCredited);
958+
final debit = int.parse(tx.amountDebited);
959+
final fee = int.tryParse(tx.fee ?? "0") ?? 0;
960+
961+
// Hack EPIC tx data into inputs and outputs.
962+
final List<OutputV2> outputs = [];
963+
final List<InputV2> inputs = [];
964+
final addressFromIsMine = myAddressesSet.contains(addressFrom);
965+
final addressToIsMine = myAddressesSet.contains(addressTo);
966+
967+
OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor(
968+
scriptPubKeyHex: "00",
969+
valueStringSats: credit.toString(),
970+
addresses: [if (addressFrom != null) addressFrom],
971+
walletOwns: true,
972+
);
973+
final InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
974+
scriptSigHex: null,
975+
scriptSigAsm: null,
976+
sequence: null,
977+
outpoint: null,
978+
addresses: [if (addressTo != null) addressTo],
979+
valueStringSats: debit.toString(),
980+
witness: null,
981+
innerRedeemScriptAsm: null,
982+
coinbase: null,
983+
walletOwns: true,
984+
);
985+
986+
final TransactionType txType;
987+
if (isIncoming) {
988+
if (addressToIsMine && addressFromIsMine) {
989+
txType = TransactionType.sentToSelf;
990+
} else {
991+
txType = TransactionType.incoming;
992+
}
993+
output = output.copyWith(
994+
addresses: [
995+
myAddressesSet
996+
.first, // Must be changed if we ever do more than a single wallet address!!!
997+
],
998+
walletOwns: true,
999+
);
1000+
} else {
1001+
txType = TransactionType.outgoing;
1002+
}
1003+
1004+
outputs.add(output);
1005+
inputs.add(input);
1006+
1007+
final otherData = {
1008+
"isEpiccashTransaction": true,
1009+
"numberOfMessages": numberOfMessages,
1010+
"slateId": slateId,
1011+
"onChainNote": onChainNote,
1012+
"isCancelled":
1013+
tx.txType == epic_models.TransactionType.TxSentCancelled ||
1014+
tx.txType == epic_models.TransactionType.TxReceivedCancelled,
1015+
"overrideFee":
1016+
Amount(
1017+
rawValue: BigInt.from(fee),
1018+
fractionDigits: cryptoCurrency.fractionDigits,
1019+
).toJsonString(),
1020+
};
1021+
1022+
final txn = TransactionV2(
1023+
walletId: walletId,
1024+
blockHash: null,
1025+
hash: commitId ?? tx.id.toString(),
1026+
txid: commitId ?? tx.id.toString(),
1027+
timestamp:
1028+
DateTime.parse(tx.creationTs).millisecondsSinceEpoch ~/ 1000,
1029+
height: tx.confirmed ? tx.kernelLookupMinHeight ?? 1 : null,
1030+
inputs: List.unmodifiable(inputs),
1031+
outputs: List.unmodifiable(outputs),
1032+
version: 0,
1033+
type: txType,
1034+
subType: TransactionSubType.none,
1035+
otherData: jsonEncode(otherData),
1036+
);
1037+
1038+
txns.add(txn);
1039+
}
1040+
1041+
await mainDB.isar.writeTxn(() async {
1042+
await mainDB.isar.transactionV2s
1043+
.where()
1044+
.walletIdEqualTo(walletId)
1045+
.deleteAll();
1046+
await mainDB.isar.transactionV2s.putAll(txns);
1047+
});
1048+
} catch (e, s) {
1049+
Logging.instance.e(
1050+
"${cryptoCurrency.runtimeType} ${cryptoCurrency.network} net wallet"
1051+
" \"${info.name}\"_${info.walletId} _updateTransactionsWithoutNodeRefresh() failed",
1052+
error: e,
1053+
stackTrace: s,
1054+
);
1055+
}
1056+
}
1057+
8991058
@override
9001059
Future<void> updateTransactions() async {
9011060
try {

0 commit comments

Comments
 (0)