use new transaction nonce property

This commit is contained in:
julian 2023-03-31 10:15:42 -06:00
parent f969179ea6
commit c8139007e3
20 changed files with 110 additions and 4 deletions

View file

@ -28,6 +28,7 @@ class Transaction {
required this.otherData, required this.otherData,
required this.inputs, required this.inputs,
required this.outputs, required this.outputs,
required this.nonce,
}); });
Tuple2<Transaction, Address?> copyWith({ Tuple2<Transaction, Address?> copyWith({
@ -46,6 +47,7 @@ class Transaction {
String? otherData, String? otherData,
List<Input>? inputs, List<Input>? inputs,
List<Output>? outputs, List<Output>? outputs,
int? nonce,
Id? id, Id? id,
Address? address, Address? address,
}) { }) {
@ -64,6 +66,7 @@ class Transaction {
isLelantus: isLelantus ?? this.isLelantus, isLelantus: isLelantus ?? this.isLelantus,
slateId: slateId ?? this.slateId, slateId: slateId ?? this.slateId,
otherData: otherData ?? this.otherData, otherData: otherData ?? this.otherData,
nonce: nonce ?? this.nonce,
inputs: inputs ?? this.inputs, inputs: inputs ?? this.inputs,
outputs: outputs ?? this.outputs) outputs: outputs ?? this.outputs)
..id = id ?? this.id, ..id = id ?? this.id,
@ -147,6 +150,7 @@ class Transaction {
"isLelantus: $isLelantus, " "isLelantus: $isLelantus, "
"slateId: $slateId, " "slateId: $slateId, "
"otherData: $otherData, " "otherData: $otherData, "
"nonce: $nonce, "
"address: ${address.value}, " "address: ${address.value}, "
"inputsLength: ${inputs.length}, " "inputsLength: ${inputs.length}, "
"outputsLength: ${outputs.length}, " "outputsLength: ${outputs.length}, "
@ -167,6 +171,7 @@ class Transaction {
"isLelantus": isLelantus, "isLelantus": isLelantus,
"slateId": slateId, "slateId": slateId,
"otherData": otherData, "otherData": otherData,
"nonce": nonce,
"address": address.value?.toJsonString(), "address": address.value?.toJsonString(),
"inputs": inputs.map((e) => e.toJsonString()).toList(), "inputs": inputs.map((e) => e.toJsonString()).toList(),
"outputs": outputs.map((e) => e.toJsonString()).toList(), "outputs": outputs.map((e) => e.toJsonString()).toList(),
@ -193,6 +198,7 @@ class Transaction {
isLelantus: json["isLelantus"] as bool?, isLelantus: json["isLelantus"] as bool?,
slateId: json["slateId"] as String?, slateId: json["slateId"] as String?,
otherData: json["otherData"] as String?, otherData: json["otherData"] as String?,
nonce: json["nonce"] as int?,
inputs: List<String>.from(json["inputs"] as List) inputs: List<String>.from(json["inputs"] as List)
.map((e) => Input.fromJsonString(e)) .map((e) => Input.fromJsonString(e))
.toList(), .toList(),

View file

@ -268,6 +268,7 @@ Transaction _transactionDeserialize(
[], [],
isCancelled: reader.readBool(offsets[5]), isCancelled: reader.readBool(offsets[5]),
isLelantus: reader.readBoolOrNull(offsets[6]), isLelantus: reader.readBoolOrNull(offsets[6]),
nonce: reader.readLongOrNull(offsets[7]),
otherData: reader.readStringOrNull(offsets[8]), otherData: reader.readStringOrNull(offsets[8]),
outputs: reader.readObjectList<Output>( outputs: reader.readObjectList<Output>(
offsets[9], offsets[9],
@ -287,7 +288,6 @@ Transaction _transactionDeserialize(
walletId: reader.readString(offsets[15]), walletId: reader.readString(offsets[15]),
); );
object.id = id; object.id = id;
object.nonce = reader.readLongOrNull(offsets[7]);
return object; return object;
} }

View file

@ -1346,6 +1346,7 @@ class BitcoinWallet extends CoinServiceAPI
isLelantus: false, isLelantus: false,
otherData: null, otherData: null,
slateId: null, slateId: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -1261,6 +1261,7 @@ class BitcoinCashWallet extends CoinServiceAPI
isLelantus: false, isLelantus: false,
otherData: null, otherData: null,
slateId: null, slateId: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );
@ -2326,6 +2327,7 @@ class BitcoinCashWallet extends CoinServiceAPI
isLelantus: false, isLelantus: false,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: null,
inputs: inputs, inputs: inputs,
outputs: outputs, outputs: outputs,
); );

View file

@ -1128,6 +1128,7 @@ class DogecoinWallet extends CoinServiceAPI
isLelantus: false, isLelantus: false,
otherData: null, otherData: null,
slateId: null, slateId: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -1699,6 +1699,7 @@ class EpicCashWallet extends CoinServiceAPI
tx["tx_type"] == "TxReceivedCancelled", tx["tx_type"] == "TxReceivedCancelled",
isLelantus: false, isLelantus: false,
slateId: slateId, slateId: slateId,
nonce: null,
otherData: tx["id"].toString(), otherData: tx["id"].toString(),
inputs: [], inputs: [],
outputs: [], outputs: [],

View file

@ -868,12 +868,26 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
Future<void> _refreshTransactions() async { Future<void> _refreshTransactions() async {
String thisAddress = await currentReceivingAddress; String thisAddress = await currentReceivingAddress;
final txsResponse = await EthereumAPI.getEthTransactions(thisAddress); final response = await EthereumAPI.getEthTransactions(thisAddress);
if (response.value == null) {
Logging.instance.log(
"Failed to refresh transactions for ${coin.prettyName} $walletName "
"$walletId: ${response.exception}",
level: LogLevel.Warning,
);
return;
}
final txsResponse =
await EthereumAPI.getEthTransactionNonces(response.value!);
if (txsResponse.value != null) { if (txsResponse.value != null) {
final allTxs = txsResponse.value!; final allTxs = txsResponse.value!;
final List<Tuple2<Transaction, Address?>> txnsData = []; final List<Tuple2<Transaction, Address?>> txnsData = [];
for (final element in allTxs) { for (final tuple in allTxs) {
final element = tuple.item1;
Amount transactionAmount = element.value; Amount transactionAmount = element.value;
bool isIncoming; bool isIncoming;
@ -909,6 +923,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
isLelantus: false, isLelantus: false,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: tuple.item2,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );
@ -966,7 +981,8 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
} }
} else { } else {
Logging.instance.log( Logging.instance.log(
"Failed to refresh transactions for ${coin.prettyName} $walletName $walletId", "Failed to refresh transactions with nonces for ${coin.prettyName} "
"$walletName $walletId: ${txsResponse.exception}",
level: LogLevel.Warning, level: LogLevel.Warning,
); );
} }

View file

@ -483,6 +483,7 @@ Future<Map<dynamic, dynamic>> staticProcessRestore(
isLelantus: true, isLelantus: true,
slateId: null, slateId: null,
otherData: txid, otherData: txid,
nonce: null,
inputs: element.inputs, inputs: element.inputs,
outputs: element.outputs, outputs: element.outputs,
)..address.value = element.address.value; )..address.value = element.address.value;
@ -914,6 +915,7 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
isLelantus: false, isLelantus: false,
otherData: null, otherData: null,
slateId: null, slateId: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );
@ -3070,6 +3072,7 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
isCancelled: false, isCancelled: false,
isLelantus: true, isLelantus: true,
slateId: null, slateId: null,
nonce: null,
otherData: transactionInfo["otherData"] as String?, otherData: transactionInfo["otherData"] as String?,
inputs: [], inputs: [],
outputs: [], outputs: [],
@ -3631,6 +3634,7 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
isLelantus: false, isLelantus: false,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: null,
inputs: ins, inputs: ins,
outputs: outs, outputs: outs,
); );
@ -4971,6 +4975,7 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
isLelantus: true, isLelantus: true,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -1253,6 +1253,7 @@ class LitecoinWallet extends CoinServiceAPI
isLelantus: false, isLelantus: false,
otherData: null, otherData: null,
slateId: null, slateId: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -937,6 +937,7 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
isLelantus: false, isLelantus: false,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -1243,6 +1243,7 @@ class NamecoinWallet extends CoinServiceAPI
isLelantus: false, isLelantus: false,
otherData: null, otherData: null,
slateId: null, slateId: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -1172,6 +1172,7 @@ class ParticlWallet extends CoinServiceAPI
isLelantus: false, isLelantus: false,
otherData: null, otherData: null,
slateId: null, slateId: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );
@ -2367,6 +2368,7 @@ class ParticlWallet extends CoinServiceAPI
outputs: outputs, outputs: outputs,
isCancelled: false, isCancelled: false,
isLelantus: false, isLelantus: false,
nonce: null,
slateId: null, slateId: null,
otherData: null, otherData: null,
); );

View file

@ -1016,6 +1016,7 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
isLelantus: false, isLelantus: false,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -11,6 +11,7 @@ import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/eth_commons.dart'; import 'package:stackwallet/utilities/eth_commons.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:tuple/tuple.dart';
class EthApiException with Exception { class EthApiException with Exception {
EthApiException(this.message); EthApiException(this.message);
@ -89,6 +90,64 @@ abstract class EthereumAPI {
} }
} }
static Future<EthereumResponse<List<Tuple2<EthTxDTO, int?>>>>
getEthTransactionNonces(
List<EthTxDTO> txns,
) async {
try {
final response = await get(
Uri.parse(
"$stackBaseServer/transactions?transactions=${txns.map((e) => e.hash).join(" ")}",
),
);
if (response.statusCode == 200) {
if (response.body.isNotEmpty) {
final json = jsonDecode(response.body) as Map;
final list = List<Map<String, dynamic>>.from(json["data"] as List);
final List<Tuple2<EthTxDTO, int?>> result = [];
for (final dto in txns) {
final data =
list.firstWhere((e) => e["hash"] == dto.hash, orElse: () => {});
final nonce = data["nonce"] as int?;
result.add(Tuple2(dto, nonce));
}
return EthereumResponse(
result,
null,
);
} else {
throw EthApiException(
"getEthTransactionNonces($txns) response is empty but status code is "
"${response.statusCode}",
);
}
} else {
throw EthApiException(
"getEthTransactionNonces($txns) failed with status code: "
"${response.statusCode}",
);
}
} on EthApiException catch (e) {
return EthereumResponse(
null,
e,
);
} catch (e, s) {
Logging.instance.log(
"getEthTransactionNonces($txns): $e\n$s",
level: LogLevel.Error,
);
return EthereumResponse(
null,
EthApiException(e.toString()),
);
}
}
static Future<EthereumResponse<List<EthTokenTxExtraDTO>>> static Future<EthereumResponse<List<EthTokenTxExtraDTO>>>
getEthTokenTransactionsByTxids(List<String> txids) async { getEthTokenTransactionsByTxids(List<String> txids) async {
try { try {

View file

@ -447,6 +447,7 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
isCancelled: false, isCancelled: false,
isLelantus: false, isLelantus: false,
slateId: null, slateId: null,
nonce: tuple.item2.nonce,
otherData: tuple.item1.address, otherData: tuple.item1.address,
inputs: [], inputs: [],
outputs: [], outputs: [],

View file

@ -231,6 +231,7 @@ mixin ElectrumXParsing {
isLelantus: false, isLelantus: false,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: null,
inputs: ins, inputs: ins,
outputs: outs, outputs: outs,
); );

View file

@ -349,6 +349,7 @@ class DbVersionMigrator with WalletDB {
isLelantus: false, isLelantus: false,
slateId: tx.slateId, slateId: tx.slateId,
otherData: tx.otherData, otherData: tx.otherData,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -108,6 +108,7 @@ void main() {
isLelantus: null, isLelantus: null,
slateId: t.slateId, slateId: t.slateId,
otherData: t.otherData, otherData: t.otherData,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
), ),

View file

@ -115,6 +115,7 @@ void main() {
isLelantus: true, isLelantus: true,
slateId: null, slateId: null,
otherData: null, otherData: null,
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
); );

View file

@ -64,6 +64,7 @@ void main() {
isLelantus: null, isLelantus: null,
slateId: '', slateId: '',
otherData: '', otherData: '',
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
)..address.value = Address( )..address.value = Address(
@ -169,6 +170,7 @@ void main() {
isLelantus: null, isLelantus: null,
slateId: '', slateId: '',
otherData: '', otherData: '',
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
)..address.value = Address( )..address.value = Address(
@ -271,6 +273,7 @@ void main() {
isLelantus: null, isLelantus: null,
slateId: '', slateId: '',
otherData: '', otherData: '',
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
)..address.value = Address( )..address.value = Address(
@ -367,6 +370,7 @@ void main() {
isLelantus: null, isLelantus: null,
slateId: '', slateId: '',
otherData: '', otherData: '',
nonce: null,
inputs: [], inputs: [],
outputs: [], outputs: [],
)..address.value = Address( )..address.value = Address(