firewallet-mobile/lib/transactions.dart

348 lines
9.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:url_launcher/url_launcher.dart';
class TransactionsPage extends StatefulWidget {
TransactionsPage({Key? key, required this.uuid, required this.wallet})
: super(key: key);
final String uuid;
String wallet;
@override
_TransactionsPageState createState() => _TransactionsPageState();
}
class _TransactionsPageState extends State<TransactionsPage> {
@override
void initState() {
super.initState();
fetchTransactions();
}
List<Transaction> transactions = [];
Future<void> fetchTransactions() async {
// Fetch domains from api
final response = await http.get(Uri.parse(
'https://api.firewallet.au/wallet/transactions?uuid=${widget.uuid}&name=${widget.wallet}'));
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
if (data is Map && data.containsKey('error')) {
print('Error: ${data['error']}');
return;
}
// Return if empty
if (data.isEmpty) {
setState(() {
transactions = [
Transaction(
confirmations: 0,
date: 'No transactions found',
hash: 'No transactions found',
inputs: [],
outputs: [])
];
});
return;
}
final txData = data as List;
setState(() {
transactions = txData
.map(
(tx) => Transaction(
confirmations: tx['confirmations'],
date: tx['date'],
hash: tx['hash'],
inputs: (tx['inputs'] as List<dynamic>).map<TXInput>((input) {
return TXInput(
address: input['address'],
value: input['value'],
path: input['path'] == null
? Path()
: Path(
account: input['path']['account'],
change: input['path']['change'],
derivation: input['path']['derivation'],
name: input['path']['name'],
));
}).toList(),
outputs:
(tx['outputs'] as List<dynamic>).map<TXOutput>((output) {
return TXOutput(
address: output['address'],
value: output['value'],
covenant: Covenant(
action: output['covenant']['action'],
type: output['covenant']['type'],
items: List<String>.from(output['covenant']['items'])),
path: output['path'] == null
? Path()
: Path(
account: output['path']['account'],
change: output['path']['change'],
derivation: output['path']['derivation'],
name: output['path']['name'],
),
);
}).toList(),
),
)
.toList();
});
print(transactions[0].hash);
print(transactions[0].value().toStringAsFixed(2));
} else {
// Handle error
print('Failed to load wallet names');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${widget.wallet} - Transactions'),
// Add refresh button to the app bar
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: fetchTransactions,
),
],
),
// Get wallet list from api and display here
body: Center(
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return ListView(
children: <Widget>[
...transactions.map((tx) {
return Card(
child: ListTile(
title: Text(tx.toString()),
subtitle: Text(tx.hash),
trailing: Text('${tx.value().toStringAsFixed(2)} HNS'),
onTap: // Open tx in browser
() => openTX(tx.hash)),
);
}),
],
);
},
),
),
);
}
Future<void> openTX(String hash) async {
// Open the transaction in the browser
final Uri url = Uri.parse('https://hns.cymon.de/tx/$hash');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
}
}
class Transaction {
final int confirmations;
final String date;
final String hash;
final List<TXInput> inputs;
final List<TXOutput> outputs;
double totalValue = 0;
bool valueCalculated = false;
Transaction(
{required this.confirmations,
required this.date,
required this.hash,
required this.inputs,
required this.outputs});
// Calculate the total value of the transaction
double value() {
if (valueCalculated) {
return totalValue / 1000000;
}
totalValue = inputs.fold(0, (prev, input) => prev + input.getValue());
totalValue += outputs.fold(0, (prev, output) => prev + output.getValue());
valueCalculated = true;
return totalValue / 1000000;
}
@override
String toString() {
if (inputs.isEmpty) {
return 'No Transactions Found';
}
int opens = 0;
int bids = 0;
int reveals = 0;
int redeems = 0;
int registers = 0;
int updates = 0;
int transfers = 0;
int finalizes = 0;
int renews = 0;
int revokes = 0;
int other = 0;
outputs.forEach((output) {
if (output.covenant.action == 'OPEN') {
opens++;
} else if (output.covenant.action == 'BID') {
bids++;
} else if (output.covenant.action == 'REVEAL') {
reveals++;
} else if (output.covenant.action == 'REDEEM') {
redeems++;
} else if (output.covenant.action == 'REGISTER') {
registers++;
} else if (output.covenant.action == 'UPDATE') {
updates++;
} else if (output.covenant.action == 'TRANSFER') {
transfers++;
} else if (output.covenant.action == 'FINALIZE') {
finalizes++;
} else if (output.covenant.action == 'RENEW') {
renews++;
} else if (output.covenant.action == 'REVOKE') {
revokes++;
} else if (output.covenant.action == 'NONE') {
other++;
} else {
other++;
print('Unknown action: ${output.covenant.action}');
}
});
String outputString = '';
if (opens > 0) {
outputString += 'Opens: $opens, ';
}
if (bids > 0) {
outputString += 'Bids: $bids, ';
}
if (reveals > 0) {
outputString += 'Reveals: $reveals, ';
}
if (redeems > 0) {
outputString += 'Redeems: $redeems, ';
}
if (registers > 0) {
outputString += 'Registers: $registers, ';
}
if (updates > 0) {
outputString += 'Updates: $updates, ';
}
if (transfers > 0) {
outputString += 'Transfers: $transfers, ';
}
if (finalizes > 0) {
outputString += 'Finalizes: $finalizes, ';
}
if (renews > 0) {
outputString += 'Renews: $renews, ';
}
if (revokes > 0) {
outputString += 'Revokes: $revokes, ';
}
// Remove trailing comma
if (outputString.isNotEmpty) {
outputString = outputString.substring(0, outputString.length - 2);
}
if (outputString.isEmpty) {
// Check if sent or received
if (value() > 0) {
outputString = 'Received HNS';
} else {
outputString = 'Sent HNS';
}
}
return outputString;
}
}
class TXInput {
final String address;
final int value;
final Path path;
TXInput({required this.address, required this.value, required this.path});
int getValue() {
if (path.userPath()) {
return value * -1;
}
return value;
}
}
class TXOutput {
final String address;
final int value;
final Covenant covenant;
final Path path;
TXOutput(
{required this.address,
required this.value,
required this.covenant,
required this.path});
int getValue() {
if (path.userPath()) {
if (covenant.action == 'BID') {
return 0;
}
return value;
}
return 0;
}
}
class Covenant {
final String action;
final int type;
final List<String> items;
Covenant({required this.action, required this.type, required this.items});
}
class Path {
int? account;
bool? change;
String? derivation;
String? name;
Path({this.account, this.change, this.derivation, this.name});
@override
String toString() {
if (name == null) {
return 'No Path Found';
}
if (derivation == null) {
return 'Path(name: $name)';
}
return 'Path(account: $account, change: $change, derivation: $derivation, name: $name)';
}
bool userPath() {
return derivation != null;
}
}