mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-04-17 19:51:58 +00:00
feat: add tezos api class
This commit is contained in:
parent
8e1449ac41
commit
c10763b4c7
2 changed files with 131 additions and 108 deletions
lib/services/coins/tezos
111
lib/services/coins/tezos/tezos_api.dart
Normal file
111
lib/services/coins/tezos/tezos_api.dart
Normal file
|
@ -0,0 +1,111 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
||||
class TezosAPI {
|
||||
static const String _baseURL = 'https://api.tzstats.com';
|
||||
|
||||
Future<List<Tuple2<Transaction, Address>>?> getTransactions(
|
||||
String walletId, String address) async {
|
||||
try {
|
||||
String transactionsCall = "$_baseURL/explorer/account/$address/operations";
|
||||
var response = jsonDecode(
|
||||
await get(Uri.parse(transactionsCall)).then((value) => value.body));
|
||||
List<Tuple2<Transaction, Address>> txs = [];
|
||||
for (var tx in response as List) {
|
||||
if (tx["type"] == "transaction") {
|
||||
TransactionType txType;
|
||||
final String myAddress = address;
|
||||
final String senderAddress = tx["sender"] as String;
|
||||
final String targetAddress = tx["receiver"] as String;
|
||||
if (senderAddress == myAddress && targetAddress == myAddress) {
|
||||
txType = TransactionType.sentToSelf;
|
||||
} else if (senderAddress == myAddress) {
|
||||
txType = TransactionType.outgoing;
|
||||
} else if (targetAddress == myAddress) {
|
||||
txType = TransactionType.incoming;
|
||||
} else {
|
||||
txType = TransactionType.unknown;
|
||||
}
|
||||
var amount = double.parse((tx["volume"] * pow(10, Coin.tezos.decimals)).toString()).toInt();
|
||||
var fee = double.parse((tx["fee"] * pow(10, Coin.tezos.decimals)).toString()).toInt();
|
||||
var theTx = Transaction(
|
||||
walletId: walletId,
|
||||
txid: tx["hash"].toString(),
|
||||
timestamp: DateTime.parse(tx["time"].toString())
|
||||
.toUtc()
|
||||
.millisecondsSinceEpoch ~/
|
||||
1000,
|
||||
type: txType,
|
||||
subType: TransactionSubType.none,
|
||||
amount: amount,
|
||||
amountString: Amount(
|
||||
rawValue:
|
||||
BigInt.parse((amount).toInt().toString()),
|
||||
fractionDigits: Coin.tezos.decimals)
|
||||
.toJsonString(),
|
||||
fee: fee,
|
||||
height: int.parse(tx["height"].toString()),
|
||||
isCancelled: false,
|
||||
isLelantus: false,
|
||||
slateId: "",
|
||||
otherData: "",
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
nonce: 0,
|
||||
numberOfMessages: null,
|
||||
);
|
||||
final AddressSubType subType;
|
||||
switch (txType) {
|
||||
case TransactionType.incoming:
|
||||
case TransactionType.sentToSelf:
|
||||
subType = AddressSubType.receiving;
|
||||
break;
|
||||
case TransactionType.outgoing:
|
||||
case TransactionType.unknown:
|
||||
subType = AddressSubType.unknown;
|
||||
break;
|
||||
}
|
||||
final theAddress = Address(
|
||||
walletId: walletId,
|
||||
value: targetAddress,
|
||||
publicKey: [],
|
||||
derivationIndex: 0,
|
||||
derivationPath: null,
|
||||
type: AddressType.unknown,
|
||||
subType: subType,
|
||||
);
|
||||
txs.add(Tuple2(theTx, theAddress));
|
||||
}
|
||||
}
|
||||
return txs;
|
||||
} catch (e) {
|
||||
Logging.instance.log(
|
||||
"Error occured while getting transactions for $address: $e",
|
||||
level: LogLevel.Error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<int?> getFeeEstimation() async {
|
||||
try {
|
||||
var api = "$_baseURL/series/op?start_date=today&collapse=1d";
|
||||
var response = jsonDecode((await get(Uri.parse(api))).body);
|
||||
double totalFees = response[0][4] as double;
|
||||
int totalTxs = response[0][8] as int;
|
||||
return ((totalFees / totalTxs * Coin.tezos.decimals).floor());
|
||||
} catch (e) {
|
||||
Logging.instance.log("Error occured while getting fee estimation: $e",
|
||||
level: LogLevel.Error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import 'package:stackwallet/models/node_model.dart';
|
|||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||
import 'package:stackwallet/networking/http.dart';
|
||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||
import 'package:stackwallet/services/coins/tezos/tezos_api.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||
|
@ -55,6 +56,8 @@ class TezosWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
|
||||
NodeModel? _xtzNode;
|
||||
|
||||
TezosAPI tezosAPI = TezosAPI();
|
||||
|
||||
NodeModel getCurrentNode() {
|
||||
return _xtzNode ??
|
||||
NodeService(secureStorageInterface: _secureStore)
|
||||
|
@ -241,17 +244,8 @@ class TezosWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
|
||||
@override
|
||||
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||
var api = "https://api.tzstats.com/series/op?start_date=today&collapse=1d";
|
||||
var response = jsonDecode((await client.get(
|
||||
url: Uri.parse(api),
|
||||
proxyInfo:
|
||||
_prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null,
|
||||
))
|
||||
.body)[0];
|
||||
double totalFees = response[4] as double;
|
||||
int totalTxs = response[8] as int;
|
||||
int feePerTx = (totalFees / totalTxs * 1000000).floor();
|
||||
|
||||
int? feePerTx = await tezosAPI.getFeeEstimation();
|
||||
feePerTx ??= 0;
|
||||
return Amount(
|
||||
rawValue: BigInt.from(feePerTx),
|
||||
fractionDigits: coin.decimals,
|
||||
|
@ -266,18 +260,9 @@ class TezosWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
|
||||
@override
|
||||
Future<FeeObject> get fees async {
|
||||
var api = "https://api.tzstats.com/series/op?start_date=today&collapse=10d";
|
||||
var response = jsonDecode((await client.get(
|
||||
url: Uri.parse(api),
|
||||
proxyInfo:
|
||||
_prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null,
|
||||
))
|
||||
.body);
|
||||
double totalFees = response[0][4] as double;
|
||||
int totalTxs = response[0][8] as int;
|
||||
int feePerTx = (totalFees / totalTxs * 1000000).floor();
|
||||
int? feePerTx = await tezosAPI.getFeeEstimation();
|
||||
feePerTx ??= 0;
|
||||
Logging.instance.log("feePerTx:$feePerTx", level: LogLevel.Info);
|
||||
// TODO: fix numberOfBlocks - Since there is only one fee no need to set blocks
|
||||
return FeeObject(
|
||||
numberOfBlocksFast: 10,
|
||||
numberOfBlocksAverage: 10,
|
||||
|
@ -514,8 +499,7 @@ class TezosWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
)
|
||||
.then((value) => value.body));
|
||||
Amount balanceInAmount = Amount(
|
||||
rawValue: BigInt.parse(response.toString()),
|
||||
fractionDigits: coin.decimals);
|
||||
rawValue: BigInt.parse(balance), fractionDigits: coin.decimals);
|
||||
_balance = Balance(
|
||||
total: balanceInAmount,
|
||||
spendable: balanceInAmount,
|
||||
|
@ -532,95 +516,23 @@ class TezosWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
}
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
String transactionsCall = "https://api.mainnet.tzkt.io/v1/accounts/"
|
||||
"${await currentReceivingAddress}/operations";
|
||||
var response = jsonDecode(await client
|
||||
.get(
|
||||
url: Uri.parse(transactionsCall),
|
||||
proxyInfo:
|
||||
_prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null,
|
||||
)
|
||||
.then((value) => value.body));
|
||||
List<Tuple2<Transaction, Address>> txs = [];
|
||||
for (var tx in response as List) {
|
||||
if (tx["type"] == "transaction") {
|
||||
TransactionType txType;
|
||||
final String myAddress = await currentReceivingAddress;
|
||||
final String senderAddress = tx["sender"]["address"] as String;
|
||||
final String targetAddress = tx["target"]["address"] as String;
|
||||
if (senderAddress == myAddress && targetAddress == myAddress) {
|
||||
txType = TransactionType.sentToSelf;
|
||||
} else if (senderAddress == myAddress) {
|
||||
txType = TransactionType.outgoing;
|
||||
} else if (targetAddress == myAddress) {
|
||||
txType = TransactionType.incoming;
|
||||
} else {
|
||||
txType = TransactionType.unknown;
|
||||
}
|
||||
|
||||
var theTx = Transaction(
|
||||
walletId: walletId,
|
||||
txid: tx["hash"].toString(),
|
||||
timestamp: DateTime.parse(tx["timestamp"].toString())
|
||||
.toUtc()
|
||||
.millisecondsSinceEpoch ~/
|
||||
1000,
|
||||
type: txType,
|
||||
subType: TransactionSubType.none,
|
||||
amount: tx["amount"] as int,
|
||||
amountString: Amount(
|
||||
rawValue:
|
||||
BigInt.parse((tx["amount"] as int).toInt().toString()),
|
||||
fractionDigits: coin.decimals)
|
||||
.toJsonString(),
|
||||
fee: tx["bakerFee"] as int,
|
||||
height: int.parse(tx["level"].toString()),
|
||||
isCancelled: false,
|
||||
isLelantus: false,
|
||||
slateId: "",
|
||||
otherData: "",
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
nonce: 0,
|
||||
numberOfMessages: null,
|
||||
);
|
||||
final AddressSubType subType;
|
||||
switch (txType) {
|
||||
case TransactionType.incoming:
|
||||
case TransactionType.sentToSelf:
|
||||
subType = AddressSubType.receiving;
|
||||
break;
|
||||
case TransactionType.outgoing:
|
||||
case TransactionType.unknown:
|
||||
subType = AddressSubType.unknown;
|
||||
break;
|
||||
}
|
||||
final theAddress = Address(
|
||||
walletId: walletId,
|
||||
value: targetAddress,
|
||||
publicKey: [],
|
||||
derivationIndex: 0,
|
||||
derivationPath: null,
|
||||
type: AddressType.unknown,
|
||||
subType: subType,
|
||||
);
|
||||
txs.add(Tuple2(theTx, theAddress));
|
||||
}
|
||||
}
|
||||
var txs =
|
||||
await tezosAPI.getTransactions(walletId, await currentReceivingAddress);
|
||||
Logging.instance.log("Transactions: $txs", level: LogLevel.Info);
|
||||
if (txs == null) {
|
||||
return;
|
||||
} else if (txs.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await db.addNewTransactionData(txs, walletId);
|
||||
}
|
||||
|
||||
Future<void> updateChainHeight() async {
|
||||
try {
|
||||
var api = "${getCurrentNode().host}/chains/main/blocks/head/header/shell";
|
||||
var jsonParsedResponse = jsonDecode(await client
|
||||
.get(
|
||||
url: Uri.parse(api),
|
||||
proxyInfo:
|
||||
_prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null,
|
||||
)
|
||||
.then((value) => value.body));
|
||||
var api =
|
||||
"${getCurrentNode().host}:${getCurrentNode().port}/chains/main/blocks/head/header/shell";
|
||||
var jsonParsedResponse =
|
||||
jsonDecode(await get(Uri.parse(api)).then((value) => value.body));
|
||||
final int intHeight = int.parse(jsonParsedResponse["level"].toString());
|
||||
Logging.instance.log("Chain height: $intHeight", level: LogLevel.Info);
|
||||
await updateCachedChainHeight(intHeight);
|
||||
|
@ -679,7 +591,7 @@ class TezosWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Failed to refresh stellar wallet $walletId: '$walletName': $e\n$s",
|
||||
"Failed to refresh tezos wallet $walletId: '$walletName': $e\n$s",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
GlobalEventBus.instance.fire(
|
||||
|
|
Loading…
Reference in a new issue