mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-18 16:44:32 +00:00
use new transaction nonce property
This commit is contained in:
parent
f969179ea6
commit
c8139007e3
20 changed files with 110 additions and 4 deletions
|
@ -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(),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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: [],
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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: [],
|
||||||
),
|
),
|
||||||
|
|
|
@ -115,6 +115,7 @@ void main() {
|
||||||
isLelantus: true,
|
isLelantus: true,
|
||||||
slateId: null,
|
slateId: null,
|
||||||
otherData: null,
|
otherData: null,
|
||||||
|
nonce: null,
|
||||||
inputs: [],
|
inputs: [],
|
||||||
outputs: [],
|
outputs: [],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue