This commit is contained in:
Hanh 2022-12-29 20:06:07 +08:00
parent 1bfec064fb
commit 413d9bf43e
27 changed files with 4168 additions and 685 deletions

3
idl/build.sh Executable file
View File

@ -0,0 +1,3 @@
flatc -d -r --gen-object-api data.fbs
mv data_fb_generated.dart ../packages/warp_api_ffi/lib
mv data_generated.rs ../native/zcash-sync/src/db

142
idl/data.fbs Normal file
View File

@ -0,0 +1,142 @@
namespace fb;
table Account {
id:uint32;
name:string;
balance:uint64;
}
table AccountVec {
accounts:[Account];
}
table Balance {
shielded:uint64;
unconfirmed_spent:uint64;
balance:uint64;
under_confirmed:uint64;
excluded:uint64;
sapling:uint64;
orchard:uint64;
}
table Height {
height:uint32;
timestamp:uint32;
}
table ShieldedNote {
id:uint32;
height:uint32;
value:uint64;
timestamp:uint32;
orchard:bool;
excluded:bool;
spent:bool;
}
table ShieldedNoteVec {
notes:[ShieldedNote];
}
table ShieldedTx {
id:uint32;
tx_id:string;
height:uint32;
short_tx_id:string;
timestamp:uint32;
name:string;
value:uint64;
address:string;
memo:string;
}
table ShieldedTxVec {
txs:[ShieldedTx];
}
table Message {
id_msg:uint32;
id_tx:uint32;
height:uint32;
timestamp:uint32;
from:string;
to:string;
subject:string;
body:string;
read:bool;
incoming:bool;
}
table MessageVec {
messages:[Message];
}
table PrevNext {
prev:uint32;
next:uint32;
}
table SendTemplate {
id:uint32;
title:string;
address:string;
amount:uint64;
fiat_amount:double;
fee_included:bool;
fiat:string;
include_reply_to:bool;
subject:string;
body:string;
}
table SendTemplateVec {
templates:[SendTemplate];
}
table Contact {
id:uint32;
name:string;
address:string;
}
table ContactVec {
contacts:[Contact];
}
table TxTimeValue {
timestamp:uint32;
value:uint64;
}
table TxTimeValueVec {
values:[TxTimeValue];
}
table Quote {
timestamp:uint32;
price:double;
}
table QuoteVec {
values:[Quote];
}
table Spending {
recipient:string;
amount:uint64;
}
table SpendingVec {
values:[Spending];
}
table AddressBalance {
index:uint32;
address:string;
balance:uint64;
}
table AddressBalanceVec {
values:[AddressBalance];
}

View File

@ -23,10 +23,9 @@ class AccountManagerState extends State<AccountManagerPage> {
initState() {
super.initState();
Future.microtask(() async {
await _accounts.refresh();
setState(() {});
await _accounts.updateTBalance();
setState(() {});
if (mounted)
setState(() {});
});
}
@ -160,7 +159,7 @@ class AccountManagerState extends State<AccountManagerPage> {
void _onDismissed(int index, Account account) async {
await _accounts.delete(account.coin, account.id);
await _accounts.refresh();
_accounts.refresh();
await active.checkAndUpdate();
}
@ -247,7 +246,7 @@ class AccountManagerState extends State<AccountManagerPage> {
}
_refresh() async {
await _accounts.refresh();
_accounts.refresh();
setState(() {});
}

View File

@ -1,4 +1,5 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'package:warp_api/data_fb_generated.dart' ;
import 'package:warp_api/types.dart';
import 'coin/coins.dart';
import 'package:mobx/mobx.dart';
@ -19,25 +20,29 @@ class Account {
final String name;
final int balance;
int tbalance = 0;
final ShareInfo? share;
Account(this.coin, this.id, this.name, this.balance, this.tbalance, this.share);
Account(this.coin, this.id, this.name, this.balance, this.tbalance);
String get address {
return id != 0 ? WarpApi.getAddress(this.coin, this.id, settings.uaType) : "";
}
}
final Account emptyAccount = Account(0, 0, "", 0, 0, null);
final Account emptyAccount = Account(0, 0, "", 0, 0);
class AccountList {
List<Account> list = [];
Future<void> refresh() async {
AccountList() {
refresh();
}
void refresh() {
List<Account> _list = [];
for (var coin in coins) {
final dbr = DbReader(coin.coin, 0);
_list.addAll(await dbr.getAccountList());
_list.addAll(WarpApi.getAccountList(coin.coin).map((a) => Account(
coin.coin, a.id, a.name!, a.balance, 0)
));
}
list = _list;
}
@ -57,9 +62,8 @@ class AccountList {
}
Future<void> changeAccountName(int coin, int id, String name) async {
final dbr = DbReader(coin, id);
await dbr.changeAccountName(name);
await refresh();
WarpApi.updateAccountName(coin, id, name);
refresh();
}
void saveActive(int coin, int id) {
@ -141,13 +145,13 @@ abstract class _ActiveAccount with Store {
}
Future<AccountId?> getAvailableId(AccountId aid) async {
final nid = await getAvailableIdForCoin(aid.coin, aid.id);
if (nid != null) return nid;
final nid = getAvailableIdForCoin(aid.coin, aid.id);
if (nid.id != 0) return nid;
for (var coin_data in settings.coins) {
// look for an account in any other coin
if (coin_data.coin != coin) {
final nid = await getAvailableIdForCoin(coin_data.coin, coin_data.active);
if (nid != null)
final nid = getAvailableIdForCoin(coin_data.coin, coin_data.active);
if (nid.id != 0)
return nid;
}
}
@ -158,9 +162,9 @@ abstract class _ActiveAccount with Store {
// check that the account still exists
// if not, pick any account
// if there are none, return 0
Future<AccountId?> getAvailableIdForCoin(int coin, int id) async {
final dbr = DbReader(coin, id);
return dbr.getAvailableAccountId();
AccountId getAvailableIdForCoin(int coin, int id) {
final newId = WarpApi.getAvailableAccountId(coin, id);
return AccountId(coin, newId);
}
@action
@ -178,15 +182,14 @@ abstract class _ActiveAccount with Store {
Future<void> refreshAccount() async {
final dbr = DbReader(coin, id);
coinDef = settings.coins[coin].def;
final db = coinDef.db;
final accounts = AccountList();
await accounts.refresh();
accounts.refresh();
account = accounts.get(coin, id);
if (id > 0) {
taddress = await dbr.getTAddr();
canPay = await dbr.getSK() != null;
taddress = WarpApi.getTAddr(coin, id);
canPay = WarpApi.getSK(coin, id).isNotEmpty;
}
showTAddr = false;
@ -199,8 +202,7 @@ abstract class _ActiveAccount with Store {
@action
Future<void> refreshTAddr() async {
final dbr = DbReader(coin, id);
taddress = await dbr.getTAddr();
taddress = WarpApi.getTAddr(coin, id);
}
@action
@ -217,11 +219,12 @@ abstract class _ActiveAccount with Store {
}
@action
Future<void> updateBalances() async {
void updateBalances() {
final dbr = DbReader(coin, id);
final initialized = balances.initialized;
final prevBalance = balances.balance;
await dbr.updateBalances(syncStatus.confirmHeight, balances);
final b = WarpApi.getBalance(coin, id, syncStatus.confirmHeight);
balances.update(b.balance, b.shielded, b.unconfirmedSpent, b.underConfirmed, b.excluded);
if (initialized && prevBalance != balances.balance) {
showBalanceNotification(prevBalance, balances.balance);
}
@ -238,13 +241,13 @@ abstract class _ActiveAccount with Store {
@action
Future<void> update() async {
await updateBalances();
updateBalances();
updateTBalance();
await poolBalances.update();
poolBalances.update();
final dbr = DbReader(coin, id);
notes = await dbr.getNotes();
txs = await dbr.getTxs();
notes = dbr.getNotes();
txs = dbr.getTxs();
messages = ObservableList.of(await dbr.getMessages());
unread = messages.where((m) => !m.read).length;
dataEpoch += 1;
@ -342,26 +345,22 @@ abstract class _ActiveAccount with Store {
}
@action
Future<void> excludeNote(Note note) async {
await coinDef.db.execute(
"UPDATE received_notes SET excluded = ?2 WHERE id_note = ?1",
[note.id, note.excluded]);
void excludeNote(Note note) {
WarpApi.updateExcluded(coin, note.id, note.excluded);
}
@action
Future<void> invertExcludedNotes() async {
await coinDef.db.execute(
"UPDATE received_notes SET excluded = NOT(COALESCE(excluded, 0)) WHERE account = ?1",
[active.id]);
void invertExcludedNotes() {
WarpApi.invertExcluded(coin, id);
notes = notes.map((n) => n.invertExcluded).toList();
}
@action
Future<void> fetchChartData() async {
final dbr = DbReader(active.coin, active.id);
pnls = await dbr.getPNL(active.id);
spendings = await dbr.getSpending(active.id);
accountBalances = await dbr.getAccountBalanceTimeSeries(active.id, active.balances?.balance ?? 0);
void fetchChartData() {
final dbr = active.dbReader;
pnls = dbr.getPNL(active.id);
spendings = dbr.getSpending(active.id);
accountBalances = dbr.getAccountBalanceTimeSeries(active.id, active.balances?.balance ?? 0);
}
@action
@ -370,7 +369,6 @@ abstract class _ActiveAccount with Store {
WarpApi.markMessageAsRead(messages[index].id, true);
messages[index] = messages[index].withRead(true);
unread = unread - 1;
print("UNREAD $unread");
}
}
@ -383,16 +381,16 @@ abstract class _ActiveAccount with Store {
unread = 0;
}
Future<int?> prevInThread(int index) async {
int prevInThread(int index) {
final message = messages[index];
final dbr = DbReader(active.coin, active.id);
return await dbr.getPrevMessage(message.subject, message.height, id);
final pn = WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
return pn.prev;
}
Future<int?> nextInThread(int index) async {
int nextInThread(int index) {
final message = messages[index];
final dbr = DbReader(active.coin, active.id);
return await dbr.getNextMessage(message.subject, message.height, id);
final pn = WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
return pn.next;
}
@action
@ -441,11 +439,9 @@ abstract class _PoolBalances with Store {
@observable int sapling = 0;
@observable int orchard = 0;
Future<void> update() async {
final dbr = DbReader(active.coin, active.id);
final saplingBalance = await dbr.getSaplingBalance();
final orchardBalance = await dbr.getOrchardBalance();
_update(active.tbalance, saplingBalance, orchardBalance);
void update() {
final b = WarpApi.getBalance(active.coin, active.id, syncStatus.confirmHeight);
_update(active.tbalance, b.sapling, b.orchard);
}
@action

View File

@ -7,6 +7,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_palette/flutter_palette.dart';
import 'package:intl/intl.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'package:warp_api/warp_api.dart';
import 'chart.dart';
@ -229,7 +230,7 @@ class BudgetChartState extends State<BudgetChart> {
builder: (context) => Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
HorizontalBarChart(active.spendings.map((s) => s.amount).toList()),
HorizontalBarChart(active.spendings.map((s) => s.amount / ZECUNIT).toList()),
BudgetTable(active.spendings)
])));
}
@ -246,16 +247,10 @@ class BudgetTable extends StatelessWidget {
final palette = getPalette(Theme.of(context).primaryColor, spendings.length);
final rows = spendings.asMap().entries.map((e) {
final style = TextStyle(color: palette[e.key], fontFeatures: [FontFeature.tabularFigures()]);
final address = e.value.address;
final shortAddress = centerTrim(address);
final addressOrContact = e.value.contact ?? shortAddress;
final recipient = e.value.recipient!;
return DataRow(cells: [
DataCell(
GestureDetector(onTap: () async {
await Clipboard.setData(ClipboardData(text: address));
showSnackBar(S.of(context).addressCopiedToClipboard);
}, child: Text(addressOrContact, style: style))),
DataCell(Text(decimalFormat(e.value.amount, 8), style: style)),
DataCell(Text(recipient, style: style)),
DataCell(Text(decimalFormat(e.value.amount / ZECUNIT, 8), style: style)),
]);
}).toList();
return DataTable(

View File

@ -2,11 +2,7 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:warp_api/warp_api.dart';
import '../main.dart';
@ -30,7 +26,6 @@ abstract class CoinBase {
String get dbName;
late String dbDir;
late String dbFullPath;
late Database db;
List<LWInstance> get lwd;
bool get supportsUA;
bool get supportsMultisig;
@ -41,21 +36,6 @@ abstract class CoinBase {
dbFullPath = _getFullPath(dbDir);
}
bool exists() => File(dbFullPath).existsSync();
Future<void> open(bool delete_wal) async {
print("Opening DB ${dbFullPath}, delete_wal = ${delete_wal}");
// schema handled in backend
db = await openDatabase(dbFullPath, onConfigure: (db) async {
if (delete_wal)
await db.rawQuery("PRAGMA journal_mode=DELETE");
});
}
Future<void> close() async {
await db.close();
}
Future<bool> tryImport(PlatformFile file) async {
if (file.name == dbName) {
final dest = p.join(settings.tempDir, dbName);
@ -78,10 +58,10 @@ abstract class CoinBase {
Future<void> export(BuildContext context, String dbPath) async {
final path = _getFullPath(dbPath);
db = await openDatabase(path, onConfigure: (db) async {
await db.rawQuery("PRAGMA journal_mode=off");
});
await db.close();
// db = await openDatabase(path, onConfigure: (db) async {
// await db.rawQuery("PRAGMA journal_mode=off");
// });
// await db.close();
await exportFile(context, path, dbName);
}
@ -99,16 +79,3 @@ abstract class CoinBase {
return path;
}
}
Future<void> createSchema(Database db, int version) async {
final script = await rootBundle.loadString("assets/create_db.sql");
final statements = script.split(";");
for (var s in statements) {
if (s.isNotEmpty) {
final sql = s.trim();
print(sql);
db.execute(sql);
}
}
}

View File

@ -7,5 +7,5 @@ CoinBase ycash = YcashCoin();
CoinBase zcash = ZcashCoin();
CoinBase zcashtest = ZcashTestCoin();
final coins = [ycash, zcash];
final coins = [zcash, ycash];

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_svg/svg.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'package:warp_api/warp_api.dart';
import 'main.dart';
@ -33,9 +34,9 @@ class ContactsState extends State<ContactsTab> {
child: Dismissible(
key: Key("${c.id}"),
child: ListTile(
title: Text(c.name,
title: Text(c.name!,
style: Theme.of(context).textTheme.headline5),
subtitle: Text(c.address),
subtitle: Text(c.address!),
trailing: Icon(Icons.chevron_right),
onTap: () { _onContact(c); },
onLongPress: () { _editContact(c); },
@ -51,32 +52,32 @@ class ContactsState extends State<ContactsTab> {
);
}
_onContact(Contact c) {
_onContact(ContactT c) {
Navigator.of(context).pushNamed('/send', arguments: SendPageArgs(contact: c));
}
_editContact(Contact c) async {
_editContact(ContactT c) async {
final contact = await showContactForm(context, c, true);
if (contact != null) {
contacts.add(contact);
}
}
Future<bool> _onConfirmDelContact(Contact c) async {
Future<bool> _onConfirmDelContact(ContactT c) async {
final confirm = await showMessageBox(context, S.of(context).deleteContact,
S.of(context).areYouSureYouWantToDeleteThisContact,
S.of(context).delete);
return confirm;
}
_delContact(Contact c) async {
await contacts.remove(c);
_delContact(ContactT c) {
contacts.remove(c);
}
Future<Contact?> showContactForm(BuildContext context, Contact c, bool edit) async {
Future<ContactT?> showContactForm(BuildContext context, ContactT c, bool edit) async {
final key = GlobalKey<ContactState>();
final contact = await showDialog<Contact>(
final contact = await showDialog<ContactT>(
context: context,
builder: (context) => AlertDialog(
contentPadding: EdgeInsets.all(16),
@ -118,7 +119,7 @@ class NoContact extends StatelessWidget {
}
class ContactForm extends StatefulWidget {
final Contact contact;
final ContactT contact;
ContactForm(this.contact, {Key? key}) : super(key: key);
@ -134,8 +135,8 @@ class ContactState extends State<ContactForm> {
@override
void initState() {
super.initState();
nameController.text = widget.contact.name;
address = widget.contact.address;
nameController.text = widget.contact.name ?? "";
address = widget.contact.address ?? "";
}
@override
@ -159,7 +160,7 @@ class ContactState extends State<ContactForm> {
final state = formKey.currentState!;
if (state.validate()) {
state.save();
final contact = Contact(widget.contact.id, nameController.text, address);
final contact = ContactT(id: widget.contact.id, name: nameController.text, address: address);
Navigator.of(context).pop(contact);
active.update();
}

View File

@ -1,13 +1,9 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:intl/intl.dart';
import 'package:sqflite/sqflite.dart';
import 'package:warp_api/types.dart';
import 'accounts.dart';
import 'package:warp_api/data_fb_generated.dart' hide Quote;
import 'store.dart';
import 'package:warp_api/warp_api.dart';
import 'package:convert/convert.dart';
import 'main.dart';
final DateFormat noteDateFormat = DateFormat("yy-MM-dd HH:mm");
@ -18,180 +14,47 @@ final DateFormat msgDateFormatFull = DateFormat("yy-MM-dd HH:mm:ss");
class DbReader {
int coin;
int id;
Database db;
DbReader(int coin, int id): this.init(coin, id, settings.coins[coin].def.db);
DbReader.init(this.coin, this.id, this.db);
DbReader(int coin, int id): this.init(coin, id);
DbReader.init(this.coin, this.id);
Future<bool> hasAccount() async {
final List<Map> res = await db.rawQuery(
"SELECT 1 FROM accounts",
[]);
return res.isNotEmpty;
}
Future<List<Account>> getAccountList() async {
List<Account> accounts = [];
final List<Map> res = await db.rawQuery(
"WITH notes AS (SELECT a.id_account, a.name, CASE WHEN r.spent IS NULL THEN r.value ELSE 0 END AS nv FROM accounts a LEFT JOIN received_notes r ON a.id_account = r.account),"
"accounts2 AS (SELECT id_account, name, COALESCE(sum(nv), 0) AS balance FROM notes GROUP by id_account) "
"SELECT a.id_account, a.name, a.balance FROM accounts2 a",
[]);
for (var r in res) {
final int id = r['id_account'];
final account = Account(
coin,
id,
r['name'],
r['balance'],
0,
null);
accounts.add(account);
}
return accounts;
}
// check that the account still exists
// if not, pick any account
// if there are none, return 0
Future<AccountId?> getAvailableAccountId() async {
final List<Map> res1 = await db.rawQuery(
"SELECT 1 FROM accounts WHERE id_account = ?1", [id]);
if (res1.isNotEmpty)
return AccountId(coin, id);
final List<Map> res2 = await db.rawQuery(
"SELECT id_account FROM accounts", []);
if (res2.isNotEmpty) {
final id = res2[0]['id_account'];
return AccountId(coin, id);
}
return null;
}
Future<String> getTAddr() async {
final List<Map> res1 = await db.rawQuery(
"SELECT address FROM taddrs WHERE account = ?1", [id]);
final taddress = res1.isNotEmpty ? res1[0]['address'] : "";
return taddress;
}
Future<String?> getSK() async {
final List<Map> res1 = await db.rawQuery(
"SELECT sk FROM accounts WHERE id_account = ?1", [id]);
final sk = res1.isNotEmpty ? res1[0]['address'] : null;
return sk;
}
Future<void> changeAccountName(String name) async {
await db.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1",
[id, name]);
}
Future<BlockInfo?> getDbSyncedHeight() async {
final rows = await db.rawQuery("SELECT height, timestamp FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)");
if (rows.isNotEmpty) {
final row = rows.first;
final height = row['height'] as int;
final timestampEpoch = row['timestamp'] as int;
final timestamp = DateTime.fromMillisecondsSinceEpoch(timestampEpoch * 1000);
final blockInfo = BlockInfo(height, timestamp);
return blockInfo;
}
return null;
}
Future<void> updateBalances(int confirmHeight, Balances balances) async {
final balance = Sqflite.firstIntValue(await db.rawQuery(
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND (spent IS NULL OR spent = 0)",
[id])) ?? 0;
final shieldedBalance = Sqflite.firstIntValue(await db.rawQuery(
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL",
[id])) ?? 0;
final unconfirmedSpentBalance = Sqflite.firstIntValue(await db.rawQuery(
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent = 0",
[id])) ?? 0;
final underConfirmedBalance = Sqflite.firstIntValue(await db.rawQuery(
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL AND height > ?2",
[id, confirmHeight])) ?? 0;
final excludedBalance = Sqflite.firstIntValue(await db.rawQuery(
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL "
"AND height <= ?2 AND excluded",
[id, confirmHeight])) ?? 0;
balances.update(balance, shieldedBalance, unconfirmedSpentBalance, underConfirmedBalance, excludedBalance);
}
Future<int> getSaplingBalance() async {
return Sqflite.firstIntValue(await db.rawQuery("SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 0",
[id])) ?? 0;
}
Future<int> getOrchardBalance() async {
return Sqflite.firstIntValue(await db.rawQuery("SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 1",
[id])) ?? 0;
}
Future<List<Note>> getNotes() async {
final List<Map> res = await db.rawQuery(
"SELECT n.id_note, n.height, n.value, t.timestamp, n.orchard, n.excluded, n.spent FROM received_notes n, transactions t "
"WHERE n.account = ?1 AND (n.spent IS NULL OR n.spent = 0) "
"AND n.tx = t.id_tx ORDER BY n.height DESC",
[id]);
final notes = res.map((row) {
final id = row['id_note'];
final height = row['height'];
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final orchard = row['orchard'] != 0;
final excluded = (row['excluded'] ?? 0) != 0;
final spent = row['spent'] == 0;
List<Note> getNotes() {
final ns = WarpApi.getNotes(coin, id);
final notes = ns.map((n) {
final timestamp = DateTime.fromMillisecondsSinceEpoch(n.timestamp * 1000);
return Note(
id, height, timestamp, row['value'] / ZECUNIT, orchard, excluded, spent);
n.id, n.height, timestamp, n.value / ZECUNIT, n.orchard, n.excluded, n.spent);
}).toList();
print("NOTES ${notes.length}");
return notes;
}
Future<List<Tx>> getTxs() async {
final List<Map> res2 = await db.rawQuery(
"SELECT id_tx, txid, height, timestamp, t.address, c.name AS cname, a.name AS aname, value, memo FROM transactions t "
"LEFT JOIN contacts c ON t.address = c.address "
"LEFT JOIN accounts a ON a.address = t.address "
"WHERE account = ?1 ORDER BY height DESC",
[id]);
final txs = res2.map((row) {
Uint8List txid = row['txid'];
final fullTxId = hex.encode(txid.reversed.toList());
final shortTxid = fullTxId.substring(0, 8);
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final String? contactName = row['cname'];
final String? accountName = row['aname'];
final name = contactName ?? accountName;
List<Tx> getTxs() {
final txs = WarpApi.getTxs(coin, id);
final transactions = txs.map((tx) {
final timestamp = DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000);
return Tx(
row['id_tx'],
row['height'],
tx.id,
tx.height,
timestamp,
shortTxid,
fullTxId,
row['value'] / ZECUNIT,
row['address'] ?? "",
name,
row['memo'] ?? "");
tx.shortTxId!,
tx.txId!,
tx.value / ZECUNIT,
tx.address!,
tx.name!,
tx.memo!);
}).toList();
print("TXS ${txs.length}");
return txs;
print("TXS ${transactions.length}");
return transactions;
}
Future<List<PnL>> getPNL(int accountId) async {
List<PnL> getPNL(int accountId) {
final range = _getChartRange();
final List<Map> res1 = await db.rawQuery(
"SELECT timestamp, value FROM transactions WHERE timestamp >= ?2 AND account = ?1",
[accountId, range.start ~/ 1000]);
final List<Trade> trades = [];
for (var row in res1) {
final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final qty = row['value'] / ZECUNIT;
for (var row in WarpApi.getPnLTxs(coin, id, range.start ~/ 1000)) {
final dt = DateTime.fromMillisecondsSinceEpoch(row.timestamp * 1000);
final qty = row.value / ZECUNIT;
trades.add(Trade(dt, qty));
}
@ -204,13 +67,10 @@ class DbReader {
(acc, t) => acc + t.qty,
0.0);
final List<Map> res2 = await db.rawQuery(
"SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1",
[settings.currency, range.start ~/ 1000]);
final List<Quote> quotes = [];
for (var row in res2) {
final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final price = row['price'];
for (var row in WarpApi.getQuotes(coin, range.start ~/ 1000, settings.currency)) {
final dt = DateTime.fromMillisecondsSinceEpoch(row.timestamp * 1000);
final price = row.price;
quotes.add(Quote(dt, price));
}
@ -243,33 +103,21 @@ class DbReader {
return pnls;
}
Future<List<Spending>> getSpending(int accountId) async {
List<Spending> getSpending(int accountId) {
final range = _getChartRange();
final List<Map> res = await db.rawQuery(
"SELECT SUM(value) as v, t.address, c.name FROM transactions t LEFT JOIN contacts c ON t.address = c.address "
"WHERE account = ?1 AND timestamp >= ?2 AND value < 0 GROUP BY t.address ORDER BY v ASC LIMIT 5",
[accountId, range.start ~/ 1000]);
final spendings = res.map((row) {
final address = row['address'] ?? "";
final value = -row['v'] / ZECUNIT;
final contact = row['name'];
return Spending(address, value, contact);
}).toList();
return spendings;
return WarpApi.getSpendings(coin, id, range.start ~/ 1000);
}
Future<List<TimeSeriesPoint<double>>> getAccountBalanceTimeSeries(int accountId, int balance) async {
List<TimeSeriesPoint<double>> getAccountBalanceTimeSeries(int accountId, int balance) {
final range = _getChartRange();
final List<Map> res = await db.rawQuery(
"SELECT timestamp, value FROM transactions WHERE account = ?1 AND timestamp >= ?2 ORDER BY timestamp DESC",
[accountId, range.start ~/ 1000]);
final trades = WarpApi.getPnLTxs(coin, id, range.start ~/ 1000);
List<AccountBalance> _accountBalances = [];
var b = balance;
_accountBalances.add(AccountBalance(DateTime.now(), b / ZECUNIT));
for (var row in res) {
for (var trade in trades) {
final timestamp =
DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final value = row['value'] as int;
DateTime.fromMillisecondsSinceEpoch(trade.timestamp * 1000);
final value = trade.value;
final ab = AccountBalance(timestamp, b / ZECUNIT);
_accountBalances.add(ab);
b -= value;
@ -288,63 +136,27 @@ class DbReader {
return accountBalances;
}
Future<List<ZMessage>> getMessages() async {
final List<Map> res = await db.rawQuery(
"SELECT m.id, m.id_tx, m.timestamp, m.sender, m.recipient, m.incoming, c.name as scontact, a.name as saccount, c2.name as rcontact, a2.name as raccount, "
"subject, body, height, read FROM messages m "
"LEFT JOIN contacts c ON m.sender = c.address "
"LEFT JOIN accounts a ON m.sender = a.address "
"LEFT JOIN contacts c2 ON m.recipient = c2.address "
"LEFT JOIN accounts a2 ON m.recipient = a2.address "
"WHERE account = ?1 ORDER BY timestamp DESC",
[id]);
List<ZMessage> messages = [];
for (var row in res) {
final id = row['id'];
final txId = row['id_tx'] ?? 0;
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final height = row['height'];
final sender = row['sender'];
final from = row['scontact'] ?? row['saccount'] ?? sender;
final recipient = row['recipient'];
final to = row['rcontact'] ?? row['raccount'] ?? recipient;
final subject = row['subject'];
final body = row['body'];
final read = row['read'] == 1;
final incoming = row['incoming'] == 1;
messages.add(ZMessage(id, txId, incoming, from, to, subject, body, timestamp, height, read));
}
return messages;
List<ZMessage> getMessages() {
final messages = WarpApi.getMessages(coin, id);
return messages.map((m) =>
ZMessage(m.idMsg, m.idTx, m.incoming, m.from, m.to!, m.subject!, m.body!,
DateTime.fromMillisecondsSinceEpoch(m.timestamp * 1000), m.height, m.read)).toList();
}
Future<int?> getPrevMessage(String subject, int height, int account) async {
final id = await Sqflite.firstIntValue(await db.rawQuery(
"SELECT MAX(id) FROM messages WHERE subject = ?1 AND height < ?2 and account = ?3",
[subject, height, account]));
return id;
}
Future<int?> getNextMessage(String subject, int height, int account) async {
final id = await Sqflite.firstIntValue(await db.rawQuery(
"SELECT MIN(id) FROM messages WHERE subject = ?1 AND height > ?2 and account = ?3",
[subject, height, account]));
return id;
}
Future<List<TAccount>> getTAccounts() async {
final List<Map> res = await db.rawQuery(
"SELECT aindex, address, value FROM taddr_scan", []);
List<TAccount> accounts = [];
for (var row in res) {
final aindex = row['aindex'];
final address = row['address'];
final balance = row['value'];
final account = TAccount(aindex, address, balance);
accounts.add(account);
}
db.execute("DELETE FROM taddr_scan");
return accounts;
}
// Future<List<TAccount>> getTAccounts() async {
// final List<Map> res = await db.rawQuery(
// "SELECT aindex, address, value FROM taddr_scan", []);
// List<TAccount> accounts = [];
// for (var row in res) {
// final aindex = row['aindex'];
// final address = row['address'];
// final balance = row['value'];
// final account = TAccount(aindex, address, balance);
// accounts.add(account);
// }
// db.execute("DELETE FROM taddr_scan");
// return accounts;
// }
TimeRange _getChartRange() {
final now = DateTime.now().toUtc();
@ -354,48 +166,9 @@ class DbReader {
return TimeRange(cutoff, today.millisecondsSinceEpoch);
}
Future<List<SendTemplateId>> loadTemplates() async {
final List<Map> res = await db.rawQuery(
"SELECT id_send_template, title FROM send_templates", []);
List<SendTemplateId> templateIds = [];
for (var row in res) {
final id = row['id_send_template'];
final title = row['title'];
final templateId = SendTemplateId(id, title);
templateIds.add(templateId);
}
return templateIds;
}
Future<SendTemplate?> loadTemplate(int id) async {
final List<Map> res = await db.rawQuery(
"SELECT title, address, amount, fiat_amount, fee_included, fiat, include_reply_to, subject, body "
"FROM send_templates WHERE id_send_template = ?1", [id]);
for (var row in res) {
final title = row['title'];
final address = row['address'];
final amount = row['amount'];
final num fiat_amount = row['fiat_amount'];
final fee_included = row['fee_included'] != 0;
final fiat = row['fiat'];
final include_reply_to = row['include_reply_to'] != 0;
final subject = row['subject'];
final body = row['body'];
final memo = Memo(include_reply_to, subject, body);
final template = SendTemplate(id, title, address, amount, fiat_amount.toDouble(), fee_included, fiat, memo);
return template;
}
}
Future<List<Contact>> getContacts() async {
List<Contact> contacts = [];
List<Map> res = await db.rawQuery(
"SELECT id, name, address FROM contacts WHERE address <> '' ORDER BY name");
for (var c in res) {
final contact = Contact(c['id'], c['name'], c['address']);
contacts.add(contact);
}
return contacts;
List<SendTemplateT> loadTemplates() {
final templates = WarpApi.getSendTemplates(coin);
return templates;
}
int _chartRangeDays() {
@ -431,10 +204,3 @@ class ZMessage {
String fromto() => incoming ? "\u{21e6} ${sender != null ? centerTrim(sender!) : ''}" : "\u{21e8} ${centerTrim(recipient)}";
}
class TAccount {
final int aindex;
final String address;
final int balance;
TAccount(this.aindex, this.address, this.balance);
}

View File

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'package:warp_api/types.dart';
import 'package:warp_api/warp_api.dart';
import 'package:badges/badges.dart';
@ -36,20 +37,20 @@ class HomeState extends State<HomePage> {
@override
void initState() {
super.initState();
contacts.fetchContacts();
Future.microtask(() async {
await syncStatus.update();
await active.updateBalances();
active.updateBalances();
await priceStore.updateChart();
await Future.delayed(Duration(seconds: 3));
await syncStatus.sync(false);
await contacts.fetchContacts();
_syncTimer?.cancel();
_syncTimer = Timer.periodic(Duration(seconds: 15), (Timer t) async {
syncStatus.sync(false);
if (active.id != 0) {
await active.updateBalances();
active.updateBalances();
}
});
@ -398,7 +399,7 @@ class HomeInnerState extends State<HomeInnerPage> with SingleTickerProviderState
_onAddContact() async {
final contact = await contactKey.currentState
?.showContactForm(context, Contact.empty(), false);
?.showContactForm(context, ContactT(), false);
if (contact != null) {
contacts.add(contact);
}

View File

@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi' show DynamicLibrary, NativePort;
import 'dart:ffi' show NativePort;
import 'dart:io';
import 'dart:math';
import 'dart:ui';
@ -23,13 +23,10 @@ import 'package:qr_flutter/qr_flutter.dart';
import 'package:rate_my_app/rate_my_app.dart';
import 'package:share_plus/share_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:warp_api/warp_api.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:uni_links/uni_links.dart';
import 'package:quick_actions/quick_actions.dart';
import 'package:sqlite3/open.dart';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'accounts.dart';
import 'animated_qr.dart';
@ -328,9 +325,6 @@ class ZWalletAppState extends State<ZWalletApp> {
);
});
}
else {
databaseFactory = createDatabaseFactoryFfi(ffiInit: sqlFfiInit);;
}
}
Future<bool> _init() async {
@ -345,8 +339,6 @@ class ZWalletAppState extends State<ZWalletApp> {
final dbPath = await getDbPath();
for (var coin in coins) {
coin.init(dbPath);
await coin.open(true);
await coin.close();
}
if (exportDb) {
@ -357,8 +349,6 @@ class ZWalletAppState extends State<ZWalletApp> {
if (recover) {
for (var coin in coins) {
await coin.importFromTemp();
await coin.open(true);
await coin.close();
}
}
print("db path $dbPath");
@ -377,10 +367,6 @@ class ZWalletAppState extends State<ZWalletApp> {
}
}
for (var coin in coins) {
await coin.open(false);
}
_setProgress(0.7, 'Restoring Active Account');
await active.restore();
@ -435,6 +421,7 @@ class ZWalletAppState extends State<ZWalletApp> {
}
void _setProgress(double v, String message) {
print(message);
progressKey.currentState?.setValue(v, message);
}
}
@ -851,23 +838,9 @@ void cancelScan(BuildContext context) {
WarpApi.cancelSync();
}
void sqlFfiInit() {
open.overrideFor(OperatingSystem.linux, _openOnLinux);
}
DynamicLibrary _openOnLinux() {
try {
final library = DynamicLibrary.open('libsqlite3.so.0');
return library;
}
catch (e) {
print("Failed to load SQLite3: $e");
rethrow;
}
}
Future<String> getDataPath() async {
String? home;
if (Platform.isAndroid) home = (await getApplicationDocumentsDirectory()).parent.path;
if (Platform.isWindows) home = Platform.environment['LOCALAPPDATA'];
if (Platform.isLinux) home = Platform.environment['XDG_DATA_HOME'];
if (Platform.isMacOS) home = Platform.environment['HOME'];
@ -887,7 +860,7 @@ Future<String> getTempPath() async {
}
Future<String> getDbPath() async {
if (isMobile()) return await getDatabasesPath();
if (Platform.isIOS) return (await getApplicationDocumentsDirectory()).path;
final h = await getDataPath();
return "$h/databases";
}

View File

@ -223,14 +223,14 @@ class MessagePageState extends State<MessagePage> {
_prevInThread() async {
final id = await active.prevInThread(index);
setState(() {
if (id != null) index = active.messages.indexWhere((m) => m.id == id);
if (id != 0) index = active.messages.indexWhere((m) => m.id == id);
});
}
_nextInThread() async {
final id = await active.nextInThread(index);
setState(() {
if (id != null) index = active.messages.indexWhere((m) => m.id == id);
if (id != 0) index = active.messages.indexWhere((m) => m.id == id);
});
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'package:warp_api/warp_api.dart';
import 'db.dart';
import 'generated/l10n.dart';
@ -10,34 +11,33 @@ class ScanTAddrPage extends StatefulWidget {
}
class ScanTAddrPageState extends State<ScanTAddrPage> {
List<TAccount>? tAccounts = null;
List<AddressBalance>? addresses = null;
@override
void initState() {
Future(() async {
await WarpApi.scanTransparentAccounts(active.coin, active.id, settings.gapLimit);
final _addresses = await WarpApi.scanTransparentAccounts(active.coin, active.id, settings.gapLimit);
final dbr = DbReader(active.coin, active.id);
final _accounts = await dbr.getTAccounts();
setState(() {
tAccounts = _accounts;
addresses = _addresses;
});
});
}
@override
Widget build(BuildContext context) {
final _accounts = tAccounts;
final _addresses = addresses;
return Scaffold(
appBar: AppBar(title: Text('Scan Transparent Addresses')),
body: (_accounts == null) ?
body: (_addresses == null) ?
Center(child: Text('Scanning addresses'))
: Column(
children: [
Expanded(child: ListView.builder(
itemCount: _accounts.length,
itemCount: _addresses.length,
itemBuilder: (BuildContext context, int index) {
final a = _accounts[index];
return ListTile(title: Text(a.address), subtitle: Text(amountToString(a.balance, MAX_PRECISION)));
final a = _addresses[index];
return ListTile(title: Text(a.address!), subtitle: Text(amountToString(a.balance, MAX_PRECISION)));
})),
ButtonBar(children: confirmButtons(this.context, onPressed))
]
@ -47,10 +47,10 @@ class ScanTAddrPageState extends State<ScanTAddrPage> {
onPressed() async {
final s = S.of(context);
final _accounts = tAccounts ?? [];
final _accounts = addresses ?? [];
for (var account in _accounts) {
final name = s.subAccountIndexOf(account.aindex, active.account.name);
WarpApi.newSubAccount(name, account.aindex, 1);
final name = s.subAccountIndexOf(account.index, active.account.name);
WarpApi.newSubAccount(name, account.index, 1);
}
Navigator.of(context).pop();
}

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:mobx/mobx.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:warp_api/data_fb_generated.dart' hide Account;
import 'accounts.dart';
import 'dualmoneyinput.dart';
import 'package:warp_api/types.dart';
@ -48,8 +49,8 @@ class SendState extends State<SendPage> {
final _fee = DEFAULT_FEE;
var _usedBalance = 0;
var _replyTo = settings.includeReplyTo;
List<SendTemplateId> _templates = [];
SendTemplateId? _template;
List<SendTemplateT> _templates = [];
SendTemplateT? _template;
var _accounts = AccountList();
void clear() {
@ -80,7 +81,7 @@ class SendState extends State<SendPage> {
}
if (widget.args?.contact != null)
_addressController.text = widget.args!.contact!.address;
_addressController.text = widget.args!.contact!.address!;
if (widget.args?.address != null)
_addressController.text = widget.args!.address!;
if (widget.args?.subject != null)
@ -94,27 +95,21 @@ class SendState extends State<SendPage> {
_setPaymentURI(uri);
});
_newBlockAutorunDispose = autorun((_) async {
final _ = active.dataEpoch;
final balances = active.balances;
final sBalance = balances.shieldedBalance;
final tBalance = active.tbalance;
final excludedBalance = balances.excludedBalance;
final underConfirmedBalance = balances.underConfirmedBalance;
int? unconfirmedBalance = unconfirmedBalanceStream.value;
setState(() {
_sBalance = sBalance;
_tBalance = tBalance;
_excludedBalance = excludedBalance;
_underConfirmedBalance = underConfirmedBalance;
_unconfirmedBalance = unconfirmedBalance ?? 0;
});
});
active.updateBalances();
final balances = active.balances;
final sBalance = balances.shieldedBalance;
final tBalance = active.tbalance;
final excludedBalance = balances.excludedBalance;
final underConfirmedBalance = balances.underConfirmedBalance;
int? unconfirmedBalance = unconfirmedBalanceStream.value;
_sBalance = sBalance;
_tBalance = tBalance;
_excludedBalance = excludedBalance;
_underConfirmedBalance = underConfirmedBalance;
_unconfirmedBalance = unconfirmedBalance ?? 0;
Future.microtask(() async {
await _accounts.refresh();
await _loadTemplates();
});
final templateIds = active.dbReader.loadTemplates();
_templates = templateIds;
}
@override
@ -144,7 +139,7 @@ class SendState extends State<SendPage> {
_memoInitialized = true;
}
var templates = _templates.map((t) => DropdownMenuItem(child: Text(t.title), value: t)).toList();
var templates = _templates.map((t) => DropdownMenuItem(child: Text(t.title!), value: t)).toList();
final addReset = _template != null ? IconButton(onPressed: _resetTemplate, icon: Icon(Icons.close)) : IconButton(onPressed: _addTemplate, icon: Icon(Icons.add));
@ -176,7 +171,7 @@ class SendState extends State<SendPage> {
_addressController.text = suggestion.name;
},
suggestionsCallback: (String pattern) {
final matchingContacts = contacts.contacts.where((c) => c.name
final matchingContacts = contacts.contacts.where((c) => c.name!
.toLowerCase()
.contains(pattern.toLowerCase())).map((c) => ContactSuggestion(c));
final matchingAccounts = _accounts.list
@ -230,7 +225,7 @@ class SendState extends State<SendPage> {
Padding(padding: EdgeInsets.all(8)),
Row(children: [
Expanded(child:
DropdownButtonFormField<SendTemplateId>(
DropdownButtonFormField<SendTemplateT>(
hint: Text(s.template),
items: templates, value: _template, onChanged: (v) {
setState(() {
@ -239,7 +234,7 @@ class SendState extends State<SendPage> {
})),
addReset,
IconButton(onPressed: _template != null ? _openTemplate : null, icon: Icon(Icons.open_in_new)),
IconButton(onPressed: _template != null ? () { _saveTemplate(_template!.id, _template!.title, true); } : null, icon: Icon(Icons.save)),
IconButton(onPressed: _template != null ? () { _saveTemplate(_template!.id, _template!.title!, true); } : null, icon: Icon(Icons.save)),
IconButton(onPressed: _template != null ? _deleteTemplate : null, icon: Icon(Icons.delete)),
]),
Padding(padding: EdgeInsets.all(8)),
@ -278,32 +273,35 @@ class SendState extends State<SendPage> {
}))) ?? false;
if (!confirmed) return;
final title = titleController.text;
final id = _saveTemplate(0, title, false);
if (id != null) {
_template = SendTemplateId(id, title);
Future.microtask(_loadTemplates);
final template = _saveTemplate(0, title, false);
if (template != null) {
_template = template;
_templates.add(template);
setState(() {});
}
}
}
int? _saveTemplate(int id, String title, bool validate) {
SendTemplateT? _saveTemplate(int id, String title, bool validate) {
final form = _formKey.currentState!;
if (validate && !form.validate()) return null;
form.save();
final memo = Memo(
_replyTo, _subjectController.text, _memoController.text);
final dualAmountController = amountInput;
if (dualAmountController == null) return null;
final template = SendTemplate(
id,
title,
_address,
stringToAmount(dualAmountController.coinAmountController.text),
parseNumber(dualAmountController.fiatAmountController.text),
dualAmountController.feeIncluded,
dualAmountController.inputInCoin ? null : _fiat,
memo);
return WarpApi.saveSendTemplate(active.coin, template);
var template = SendTemplateT(
id: id,
title: title,
address: _address,
amount: stringToAmount(dualAmountController.coinAmountController.text),
fiatAmount: parseNumber(dualAmountController.fiatAmountController.text),
feeIncluded: dualAmountController.feeIncluded,
fiat: dualAmountController.inputInCoin ? null : _fiat,
includeReplyTo: _replyTo,
subject: _subjectController.text,
body: _subjectController.text);
final id2 = WarpApi.saveSendTemplate(active.coin, template);
template.id = id2;
return template;
}
Future<void> _deleteTemplate() async {
@ -318,19 +316,19 @@ class SendState extends State<SendPage> {
Future<void> _openTemplate() async {
final tid = _template;
if (tid == null) return;
final template = await active.dbReader.loadTemplate(tid.id);
final template = _template;;
if (template == null) return;
amountInput?.restore(template.amount, template.fiat_amount, template.fee_included, template.fiat);
amountInput?.restore(template.amount, template.fiatAmount, template.feeIncluded, template.fiat);
setState(() {
_addressController.text = template.address;
_replyTo = template.memo.include_reply_to;
_subjectController.text = template.memo.subject;
_memoController.text = template.memo.body;
_addressController.text = template.address!;
_replyTo = template.includeReplyTo;
_subjectController.text = template.subject!;
_memoController.text = template.body!;
});
}
Future<void> _loadTemplates() async {
final templateIds = await active.dbReader.loadTemplates();
void _loadTemplates() {
final templateIds = active.dbReader.loadTemplates();
setState(() {
_templates = templateIds;
});
@ -523,12 +521,12 @@ abstract class Suggestion {
}
class ContactSuggestion extends Suggestion {
final Contact contact;
final ContactT contact;
ContactSuggestion(this.contact);
String get name => contact.name;
String get address => contact.address;
String get name => contact.name!;
String get address => contact.address!;
}
class AccountSuggestion extends Suggestion {

View File

@ -71,19 +71,7 @@ class SettingsState extends State<SettingsPage>
FormBuilderFieldOption(
child: Text(s.advanced), value: 'advanced'),
]),
if (!simpleMode)
TabBar(
controller: _tabController,
tabs: [Tab(text: "Zcash"), Tab(text: "Ycash")]),
if (!simpleMode)
SizedBox(
height: 270,
child: TabBarView(
controller: _tabController,
children: [
ServerSelect(0),
ServerSelect(1)
])),
ServerSelect(active.coin),
FormBuilderRadioGroup(
orientation: OptionsOrientation.horizontal,
name: 'themes',

View File

@ -1,7 +1,6 @@
import 'dart:io';
import 'dart:isolate';
import 'dart:math';
import 'package:YWallet/db.dart';
import 'package:YWallet/src/version.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
@ -9,6 +8,7 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'coin/coins.dart';
import 'package:warp_api/warp_api.dart';
import 'package:warp_api/types.dart';
@ -466,7 +466,7 @@ abstract class _Settings with Store {
currency = newCurrency;
prefs.setString('currency', currency);
await priceStore.fetchCoinPrice(active.coin);
await active.fetchChartData();
active.fetchChartData();
}
@action
@ -646,7 +646,7 @@ abstract class _PriceStore with Store {
if (_lastChartUpdateTime == null || now > _lastChartUpdateTime + 5 * 60) {
await fetchCoinPrice(active.coin);
await WarpApi.syncHistoricalPrices(settings.currency);
await active.fetchChartData();
active.fetchChartData();
lastChartUpdateTime = _lastChartUpdateTime;
}
}
@ -729,9 +729,12 @@ abstract class _SyncStatus with Store {
WarpApi.skipToLastHeight(coin);
}
Future<BlockInfo?> getDbSyncedHeight() async {
final dbr = active.dbReader;
return await dbr.getDbSyncedHeight();
BlockInfo? getDbSyncedHeight() {
final h = WarpApi.getDbHeight(active.coin);
if (h == null) return null;
final timestamp = DateTime.fromMillisecondsSinceEpoch(h.timestamp * 1000);
final blockInfo = BlockInfo(h.height, timestamp);
return blockInfo;
}
@action
@ -779,7 +782,7 @@ abstract class _SyncStatus with Store {
if (currentSyncedHeight != syncedHeight) {
await active.update();
await priceStore.updateChart();
await contacts.fetchContacts();
contacts.fetchContacts();
}
}
else if (res == 1) {
@ -914,28 +917,27 @@ class ContactStore = _ContactStore with _$ContactStore;
abstract class _ContactStore with Store {
@observable
ObservableList<Contact> contacts = ObservableList<Contact>.of([]);
ObservableList<ContactT> contacts = ObservableList<ContactT>.of([]);
@action
Future<void> fetchContacts() async {
final dbr = active.dbReader;
void fetchContacts() {
contacts.clear();
contacts.addAll(await dbr.getContacts());
contacts.addAll(WarpApi.getContacts(active.coin));
}
@action
Future<void> add(Contact c) async {
WarpApi.storeContact(c.id, c.name, c.address, true);
void add(ContactT c) {
WarpApi.storeContact(c.id, c.name!, c.address!, true);
markContactsSaved(active.coin, false);
await fetchContacts();
fetchContacts();
}
@action
Future<void> remove(Contact c) async {
void remove(ContactT c) {
contacts.removeWhere((contact) => contact.id == c.id);
WarpApi.storeContact(c.id, c.name, "", true);
WarpApi.storeContact(c.id, c.name!, "", true);
markContactsSaved(active.coin, false);
await fetchContacts();
fetchContacts();
}
markContactsSaved(int coin, bool v) {
@ -1016,14 +1018,6 @@ class Tx extends HasHeight {
this.address, this.contact, this.memo);
}
class Spending {
final String address;
final double amount;
final String? contact;
Spending(this.address, this.amount, this.contact);
}
class AccountBalance {
final DateTime time;
final double balance;
@ -1031,16 +1025,6 @@ class AccountBalance {
AccountBalance(this.time, this.balance);
}
class Contact {
final int id;
final String name;
final String address;
Contact(this.id, this.name, this.address);
factory Contact.empty() => Contact(0, "", "");
}
enum SortOrder {
Unsorted,
Ascending,
@ -1136,7 +1120,7 @@ class DecodedPaymentURI {
class SendPageArgs {
final bool isMulti;
final Contact? contact;
final ContactT? contact;
final String? address;
final String? subject;
final String? uri;

View File

@ -12,7 +12,6 @@ import network_info_plus_macos
import path_provider_macos
import share_plus_macos
import shared_preferences_macos
import sqflite
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
@ -23,6 +22,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View File

@ -7,9 +7,6 @@ PODS:
- FlutterMacOS
- ReachabilitySwift
- FlutterMacOS (1.0.0)
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- network_info_plus_macos (0.0.1):
- FlutterMacOS
- path_provider_macos (0.0.1):
@ -19,9 +16,6 @@ PODS:
- FlutterMacOS
- shared_preferences_macos (0.0.1):
- FlutterMacOS
- sqflite (0.0.2):
- FlutterMacOS
- FMDB (>= 2.7.5)
- url_launcher_macos (0.0.1):
- FlutterMacOS
@ -34,12 +28,10 @@ DEPENDENCIES:
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
SPEC REPOS:
trunk:
- FMDB
- ReachabilitySwift
EXTERNAL SOURCES:
@ -59,8 +51,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
shared_preferences_macos:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
sqflite:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
@ -69,13 +59,11 @@ SPEC CHECKSUMS:
awesome_notifications: 428f5c15a700b117418aed09e29c21c5806fcf69
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
network_info_plus_macos: d2b9e6c01c291449b91a584217aa53b113847dbd
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c

@ -1 +1 @@
Subproject commit cd1d84b25d560644ab8933103a881e8a6b3781c7
Subproject commit 4168ee4fa223c6dd88a49b5337309a32c14ebeeb

File diff suppressed because it is too large Load Diff

View File

@ -183,49 +183,3 @@ class AGEKeys {
Map<String, dynamic> toJson() => _$AGEKeysToJson(this);
}
@JsonSerializable()
class Memo {
final bool include_reply_to;
final String subject;
final String body;
Memo(this.include_reply_to, this.subject, this.body);
factory Memo.fromJson(Map<String, dynamic> json) =>
_$MemoFromJson(json);
Map<String, dynamic> toJson() => _$MemoToJson(this);
}
@JsonSerializable()
class SendTemplate {
final int id;
final String title;
final String address;
final int amount;
final double fiat_amount;
final bool fee_included;
final String? fiat;
final Memo memo;
SendTemplate(this.id, this.title, this.address, this.amount, this.fiat_amount, this.fee_included, this.fiat, this.memo);
factory SendTemplate.fromJson(Map<String, dynamic> json) =>
_$SendTemplateFromJson(json);
Map<String, dynamic> toJson() => _$SendTemplateToJson(this);
}
class SendTemplateId {
final int id;
final String title;
SendTemplateId(this.id, this.title);
@override
operator ==(o) =>
o is SendTemplateId &&
o.id == id &&
o.title == title;
@override
int get hashCode => Object.hash(id, title);
}

View File

@ -7,7 +7,8 @@ import 'dart:isolate';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:ffi/ffi.dart';
import './warp_api_generated.dart';
import 'warp_api_generated.dart';
import 'data_fb_generated.dart';
import 'types.dart';
typedef report_callback = Void Function(Int32);
@ -27,6 +28,16 @@ Pointer<Int8> toNative(String s) {
return s.toNativeUtf8().cast<Int8>();
}
Pointer<Uint8> toNativeBytes(Uint8List bytes) {
final len = bytes.length;
final ptr = malloc.allocate<Uint8>(bytes.length);
final list = ptr.asTypedList(bytes.length);
for (var i = 0; i < len; i++) {
list[i] = bytes[i];
}
return ptr;
}
int unwrapResultU8(CResult_u8 r) {
if (r.error != nullptr) throw convertCString(r.error);
return r.value;
@ -47,6 +58,11 @@ String unwrapResultString(CResult_____c_char r) {
return convertCString(r.value);
}
List<int> unwrapResultBytes(CResult______u8 r) {
if (r.error != nullptr) throw convertCString(r.error);
return convertBytes(r.value, r.len);
}
class WarpApi {
static const MethodChannel _channel = const MethodChannel('warp_api');
@ -218,9 +234,9 @@ class WarpApi {
return unwrapResultString(txPlan);
}
static Future<void> scanTransparentAccounts(
static Future<List<AddressBalance>> scanTransparentAccounts(
int coin, int account, int gapLimit) async {
await compute(scanTransparentAccountsParamsIsolateFn,
return await compute(scanTransparentAccountsParamsIsolateFn,
ScanTransparentAccountsParams(gapLimit));
}
@ -261,14 +277,6 @@ class WarpApi {
return unwrapResultString(res);
}
static int saveSendTemplate(int coin, SendTemplate template) {
final templateAsJson = jsonEncode(template);
return unwrapResultU32(warp_api_lib.save_send_template(coin, toNative(templateAsJson)));
}
static void deleteSendTemplate(int coin, int id) {
warp_api_lib.delete_send_template(coin, id);
}
// static String ledgerSign(int coin, String txFilename) {
// final res = warp_api_lib.ledger_sign(coin, txFilename.toNativeUtf8().cast<Int8>());
// return res.cast<Utf8>().toDartString();
@ -373,7 +381,6 @@ class WarpApi {
static String getTxSummary(String tx) {
return unwrapResultString(
warp_api_lib.get_tx_summary(tx.toNativeUtf8().cast<Int8>()));
// TODO: Free
}
static String getBestServer(List<String> urls) {
@ -413,6 +420,124 @@ class WarpApi {
static void useGPU(bool v) {
warp_api_lib.use_gpu(v ? 1 : 0);
}
static List<Account> getAccountList(int coin) {
final r = unwrapResultBytes(warp_api_lib.get_account_list(coin));
return AccountVec(r).accounts!;
}
static int getAvailableAccountId(int coin, int id) {
return unwrapResultU32(warp_api_lib.get_available_account_id(coin, id));
}
static String getTAddr(int coin, int id) {
return unwrapResultString(warp_api_lib.get_t_addr(coin, id));
}
static String getSK(int coin, int id) {
return unwrapResultString(warp_api_lib.get_sk(coin, id));
}
static void updateAccountName(int coin, int id, String name) {
warp_api_lib.update_account_name(coin, id, toNative(name));
}
static Balance getBalance(int coin, int id, int confirmedHeight) {
final r = unwrapResultBytes(warp_api_lib.get_balances(coin, id, confirmedHeight));
final b = Balance(r);
return b;
}
static Height? getDbHeight(int coin) {
final r = unwrapResultBytes(warp_api_lib.get_db_height(coin));
if (r.isEmpty) return null;
final h = Height(r);
return h;
}
static List<ShieldedNote> getNotes(int coin, int id) {
final r = unwrapResultBytes(warp_api_lib.get_notes(coin, id));
final ns = ShieldedNoteVec(r);
return ns.notes!;
}
static List<ShieldedTx> getTxs(int coin, int id) {
final r = unwrapResultBytes(warp_api_lib.get_txs(coin, id));
final txs = ShieldedTxVec(r);
return txs.txs!;
}
static List<Message> getMessages(int coin, int id) {
final r = unwrapResultBytes(warp_api_lib.get_messages(coin, id));
final msgs = MessageVec(r);
return msgs.messages!;
}
static PrevNext getPrevNextMessage(int coin, int id, String subject, int height) {
final r = unwrapResultBytes(warp_api_lib.get_prev_next_message(coin, id, toNative(subject), height));
final pn = PrevNext(r);
return pn;
}
static List<SendTemplateT> getSendTemplates(int coin) {
final r = unwrapResultBytes(warp_api_lib.get_templates(coin));
final templates = SendTemplateVec(r).unpack();
return templates.templates!;
}
static int saveSendTemplate(int coin, SendTemplateT t) {
final template = SendTemplateObjectBuilder(
title: t.title,
address: t.address,
amount: t.amount,
feeIncluded: t.feeIncluded,
fiatAmount: t.fiatAmount,
fiat: t.fiat,
includeReplyTo: t.includeReplyTo,
subject: t.subject,
body: t.body,
).toBytes();
print("templ $t");
final data = toNativeBytes(template);
return unwrapResultU32(warp_api_lib.save_send_template(coin, data, template.length));
}
static void deleteSendTemplate(int coin, int id) {
warp_api_lib.delete_send_template(coin, id);
}
static List<ContactT> getContacts(int coin) {
final r = unwrapResultBytes(warp_api_lib.get_contacts(coin));
final contacts = ContactVec(r).unpack();
return contacts.contacts!;
}
static List<TxTimeValue> getPnLTxs(int coin, int id, int timestamp) {
final r = unwrapResultBytes(warp_api_lib.get_pnl_txs(coin, id, timestamp));
final txs = TxTimeValueVec(r);
return txs.values!;
}
static List<Quote> getQuotes(int coin, int timestamp, String currency) {
final r = unwrapResultBytes(warp_api_lib.get_historical_prices(coin, timestamp, toNative(currency)));
final quotes = QuoteVec(r);
return quotes.values!;
}
static List<Spending> getSpendings(int coin, int id, int timestamp) {
final r = unwrapResultBytes(warp_api_lib.get_spendings(coin, id, timestamp));
final quotes = SpendingVec(r);
return quotes.values!;
}
static void updateExcluded(int coin, int id, bool excluded) {
unwrapResultU8(warp_api_lib.update_excluded(coin, id, excluded ? 1 : 0));
}
static void invertExcluded(int coin, int id) {
unwrapResultU8(warp_api_lib.invert_excluded(coin, id));
}
}
String signOnlyIsolateFn(SignOnlyParams params) {
@ -450,9 +575,11 @@ int getBlockHeightByTimeIsolateFn(BlockHeightByTimeParams params) {
return unwrapResultU32(warp_api_lib.get_block_by_time(params.time));
}
void scanTransparentAccountsParamsIsolateFn(
List<AddressBalance> scanTransparentAccountsParamsIsolateFn(
ScanTransparentAccountsParams params) {
return warp_api_lib.scan_transparent_accounts(params.gapLimit);
final r = unwrapResultBytes(warp_api_lib.scan_transparent_accounts(params.gapLimit));
final v = AddressBalanceVec(r);
return v.values!;
}
class SyncParams {
@ -532,6 +659,12 @@ String convertCString(Pointer<Int8> s) {
return str;
}
List<int> convertBytes(Pointer<Uint8> s, int len) {
final bytes = [...s.asTypedList(len)];
warp_api_lib.deallocate_bytes(s, len);
return bytes;
}
void mempoolRunIsolateFn(int port) {
warp_api_lib.mempool_run(port);
}

View File

@ -53,6 +53,21 @@ class NativeLibrary {
late final _dart_deallocate_str _deallocate_str =
_deallocate_str_ptr.asFunction<_dart_deallocate_str>();
void deallocate_bytes(
ffi.Pointer<ffi.Uint8> ptr,
int len,
) {
return _deallocate_bytes(
ptr,
len,
);
}
late final _deallocate_bytes_ptr =
_lookup<ffi.NativeFunction<_c_deallocate_bytes>>('deallocate_bytes');
late final _dart_deallocate_bytes _deallocate_bytes =
_deallocate_bytes_ptr.asFunction<_dart_deallocate_bytes>();
CResult_u8 init_wallet(
int coin,
ffi.Pointer<ffi.Int8> db_path,
@ -494,7 +509,7 @@ class NativeLibrary {
late final _dart_shield_taddr _shield_taddr =
_shield_taddr_ptr.asFunction<_dart_shield_taddr>();
void scan_transparent_accounts(
CResult______u8 scan_transparent_accounts(
int gap_limit,
) {
return _scan_transparent_accounts(
@ -892,13 +907,197 @@ class NativeLibrary {
late final _dart_derive_zip32 _derive_zip32 =
_derive_zip32_ptr.asFunction<_dart_derive_zip32>();
CResult______u8 get_account_list(
int coin,
) {
return _get_account_list(
coin,
);
}
late final _get_account_list_ptr =
_lookup<ffi.NativeFunction<_c_get_account_list>>('get_account_list');
late final _dart_get_account_list _get_account_list =
_get_account_list_ptr.asFunction<_dart_get_account_list>();
CResult_u32 get_available_account_id(
int coin,
int id,
) {
return _get_available_account_id(
coin,
id,
);
}
late final _get_available_account_id_ptr =
_lookup<ffi.NativeFunction<_c_get_available_account_id>>(
'get_available_account_id');
late final _dart_get_available_account_id _get_available_account_id =
_get_available_account_id_ptr
.asFunction<_dart_get_available_account_id>();
CResult_____c_char get_t_addr(
int coin,
int id,
) {
return _get_t_addr(
coin,
id,
);
}
late final _get_t_addr_ptr =
_lookup<ffi.NativeFunction<_c_get_t_addr>>('get_t_addr');
late final _dart_get_t_addr _get_t_addr =
_get_t_addr_ptr.asFunction<_dart_get_t_addr>();
CResult_____c_char get_sk(
int coin,
int id,
) {
return _get_sk(
coin,
id,
);
}
late final _get_sk_ptr = _lookup<ffi.NativeFunction<_c_get_sk>>('get_sk');
late final _dart_get_sk _get_sk = _get_sk_ptr.asFunction<_dart_get_sk>();
CResult_u8 update_account_name(
int coin,
int id,
ffi.Pointer<ffi.Int8> name,
) {
return _update_account_name(
coin,
id,
name,
);
}
late final _update_account_name_ptr =
_lookup<ffi.NativeFunction<_c_update_account_name>>(
'update_account_name');
late final _dart_update_account_name _update_account_name =
_update_account_name_ptr.asFunction<_dart_update_account_name>();
CResult______u8 get_balances(
int coin,
int id,
int confirmed_height,
) {
return _get_balances(
coin,
id,
confirmed_height,
);
}
late final _get_balances_ptr =
_lookup<ffi.NativeFunction<_c_get_balances>>('get_balances');
late final _dart_get_balances _get_balances =
_get_balances_ptr.asFunction<_dart_get_balances>();
CResult______u8 get_db_height(
int coin,
) {
return _get_db_height(
coin,
);
}
late final _get_db_height_ptr =
_lookup<ffi.NativeFunction<_c_get_db_height>>('get_db_height');
late final _dart_get_db_height _get_db_height =
_get_db_height_ptr.asFunction<_dart_get_db_height>();
CResult______u8 get_notes(
int coin,
int id,
) {
return _get_notes(
coin,
id,
);
}
late final _get_notes_ptr =
_lookup<ffi.NativeFunction<_c_get_notes>>('get_notes');
late final _dart_get_notes _get_notes =
_get_notes_ptr.asFunction<_dart_get_notes>();
CResult______u8 get_txs(
int coin,
int id,
) {
return _get_txs(
coin,
id,
);
}
late final _get_txs_ptr = _lookup<ffi.NativeFunction<_c_get_txs>>('get_txs');
late final _dart_get_txs _get_txs = _get_txs_ptr.asFunction<_dart_get_txs>();
CResult______u8 get_messages(
int coin,
int id,
) {
return _get_messages(
coin,
id,
);
}
late final _get_messages_ptr =
_lookup<ffi.NativeFunction<_c_get_messages>>('get_messages');
late final _dart_get_messages _get_messages =
_get_messages_ptr.asFunction<_dart_get_messages>();
CResult______u8 get_prev_next_message(
int coin,
int id,
ffi.Pointer<ffi.Int8> subject,
int height,
) {
return _get_prev_next_message(
coin,
id,
subject,
height,
);
}
late final _get_prev_next_message_ptr =
_lookup<ffi.NativeFunction<_c_get_prev_next_message>>(
'get_prev_next_message');
late final _dart_get_prev_next_message _get_prev_next_message =
_get_prev_next_message_ptr.asFunction<_dart_get_prev_next_message>();
CResult______u8 get_templates(
int coin,
) {
return _get_templates(
coin,
);
}
late final _get_templates_ptr =
_lookup<ffi.NativeFunction<_c_get_templates>>('get_templates');
late final _dart_get_templates _get_templates =
_get_templates_ptr.asFunction<_dart_get_templates>();
CResult_u32 save_send_template(
int coin,
ffi.Pointer<ffi.Int8> template_,
ffi.Pointer<ffi.Uint8> template_,
int len,
) {
return _save_send_template(
coin,
template_,
len,
);
}
@ -923,6 +1122,103 @@ class NativeLibrary {
late final _dart_delete_send_template _delete_send_template =
_delete_send_template_ptr.asFunction<_dart_delete_send_template>();
CResult______u8 get_contacts(
int coin,
) {
return _get_contacts(
coin,
);
}
late final _get_contacts_ptr =
_lookup<ffi.NativeFunction<_c_get_contacts>>('get_contacts');
late final _dart_get_contacts _get_contacts =
_get_contacts_ptr.asFunction<_dart_get_contacts>();
CResult______u8 get_pnl_txs(
int coin,
int id,
int timestamp,
) {
return _get_pnl_txs(
coin,
id,
timestamp,
);
}
late final _get_pnl_txs_ptr =
_lookup<ffi.NativeFunction<_c_get_pnl_txs>>('get_pnl_txs');
late final _dart_get_pnl_txs _get_pnl_txs =
_get_pnl_txs_ptr.asFunction<_dart_get_pnl_txs>();
CResult______u8 get_historical_prices(
int coin,
int timestamp,
ffi.Pointer<ffi.Int8> currency,
) {
return _get_historical_prices(
coin,
timestamp,
currency,
);
}
late final _get_historical_prices_ptr =
_lookup<ffi.NativeFunction<_c_get_historical_prices>>(
'get_historical_prices');
late final _dart_get_historical_prices _get_historical_prices =
_get_historical_prices_ptr.asFunction<_dart_get_historical_prices>();
CResult______u8 get_spendings(
int coin,
int id,
int timestamp,
) {
return _get_spendings(
coin,
id,
timestamp,
);
}
late final _get_spendings_ptr =
_lookup<ffi.NativeFunction<_c_get_spendings>>('get_spendings');
late final _dart_get_spendings _get_spendings =
_get_spendings_ptr.asFunction<_dart_get_spendings>();
CResult_u8 update_excluded(
int coin,
int id,
int excluded,
) {
return _update_excluded(
coin,
id,
excluded,
);
}
late final _update_excluded_ptr =
_lookup<ffi.NativeFunction<_c_update_excluded>>('update_excluded');
late final _dart_update_excluded _update_excluded =
_update_excluded_ptr.asFunction<_dart_update_excluded>();
CResult_u8 invert_excluded(
int coin,
int id,
) {
return _invert_excluded(
coin,
id,
);
}
late final _invert_excluded_ptr =
_lookup<ffi.NativeFunction<_c_invert_excluded>>('invert_excluded');
late final _dart_invert_excluded _invert_excluded =
_invert_excluded_ptr.asFunction<_dart_invert_excluded>();
int has_cuda() {
return _has_cuda();
}
@ -965,6 +1261,9 @@ class CResult_u8 extends ffi.Struct {
external int value;
external ffi.Pointer<ffi.Int8> error;
@ffi.Uint32()
external int len;
}
class CResult_u32 extends ffi.Struct {
@ -972,12 +1271,18 @@ class CResult_u32 extends ffi.Struct {
external int value;
external ffi.Pointer<ffi.Int8> error;
@ffi.Uint32()
external int len;
}
class CResult_____c_char extends ffi.Struct {
external ffi.Pointer<ffi.Int8> value;
external ffi.Pointer<ffi.Int8> error;
@ffi.Uint32()
external int len;
}
class CResult_u64 extends ffi.Struct {
@ -985,6 +1290,18 @@ class CResult_u64 extends ffi.Struct {
external int value;
external ffi.Pointer<ffi.Int8> error;
@ffi.Uint32()
external int len;
}
class CResult______u8 extends ffi.Struct {
external ffi.Pointer<ffi.Uint8> value;
external ffi.Pointer<ffi.Int8> error;
@ffi.Uint32()
external int len;
}
const int QR_DATA_SIZE = 256;
@ -993,6 +1310,92 @@ const int MAX_ATTEMPTS = 10;
const int N = 200000;
const int Account_VT_ID = 4;
const int Account_VT_NAME = 6;
const int Account_VT_BALANCE = 8;
const int AccountVec_VT_ACCOUNTS = 4;
const int Balance_VT_SHIELDED = 4;
const int Balance_VT_UNCONFIRMED_SPENT = 6;
const int Balance_VT_UNDER_CONFIRMED = 10;
const int Balance_VT_EXCLUDED = 12;
const int Balance_VT_SAPLING = 14;
const int Balance_VT_ORCHARD = 16;
const int Height_VT_HEIGHT = 4;
const int Height_VT_TIMESTAMP = 6;
const int ShieldedNote_VT_VALUE = 8;
const int ShieldedNote_VT_SPENT = 16;
const int ShieldedNoteVec_VT_NOTES = 4;
const int ShieldedTx_VT_TX_ID = 6;
const int ShieldedTx_VT_SHORT_TX_ID = 10;
const int ShieldedTx_VT_ADDRESS = 18;
const int ShieldedTx_VT_MEMO = 20;
const int ShieldedTxVec_VT_TXS = 4;
const int Message_VT_ID_MSG = 4;
const int Message_VT_ID_TX = 6;
const int Message_VT_FROM = 12;
const int Message_VT_TO = 14;
const int Message_VT_SUBJECT = 16;
const int Message_VT_BODY = 18;
const int Message_VT_READ = 20;
const int Message_VT_INCOMING = 22;
const int MessageVec_VT_MESSAGES = 4;
const int PrevNext_VT_PREV = 4;
const int PrevNext_VT_NEXT = 6;
const int SendTemplate_VT_TITLE = 6;
const int SendTemplate_VT_AMOUNT = 10;
const int SendTemplate_VT_FIAT_AMOUNT = 12;
const int SendTemplate_VT_FEE_INCLUDED = 14;
const int SendTemplate_VT_FIAT = 16;
const int SendTemplate_VT_INCLUDE_REPLY_TO = 18;
const int SendTemplateVec_VT_TEMPLATES = 4;
const int ContactVec_VT_CONTACTS = 4;
const int TxTimeValueVec_VT_VALUES = 4;
const int Quote_VT_PRICE = 6;
const int Spending_VT_RECIPIENT = 4;
const int AddressBalance_VT_INDEX = 4;
typedef _c_dummy_export = ffi.Void Function();
typedef _dart_dummy_export = void Function();
@ -1013,6 +1416,16 @@ typedef _dart_deallocate_str = void Function(
ffi.Pointer<ffi.Int8> s,
);
typedef _c_deallocate_bytes = ffi.Void Function(
ffi.Pointer<ffi.Uint8> ptr,
ffi.Uint32 len,
);
typedef _dart_deallocate_bytes = void Function(
ffi.Pointer<ffi.Uint8> ptr,
int len,
);
typedef _c_init_wallet = CResult_u8 Function(
ffi.Uint8 coin,
ffi.Pointer<ffi.Int8> db_path,
@ -1305,11 +1718,11 @@ typedef _dart_shield_taddr = CResult_____c_char Function(
int confirmations,
);
typedef _c_scan_transparent_accounts = ffi.Void Function(
typedef _c_scan_transparent_accounts = CResult______u8 Function(
ffi.Uint32 gap_limit,
);
typedef _dart_scan_transparent_accounts = void Function(
typedef _dart_scan_transparent_accounts = CResult______u8 Function(
int gap_limit,
);
@ -1563,14 +1976,138 @@ typedef _dart_derive_zip32 = CResult_____c_char Function(
int address,
);
typedef _c_get_account_list = CResult______u8 Function(
ffi.Uint8 coin,
);
typedef _dart_get_account_list = CResult______u8 Function(
int coin,
);
typedef _c_get_available_account_id = CResult_u32 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
);
typedef _dart_get_available_account_id = CResult_u32 Function(
int coin,
int id,
);
typedef _c_get_t_addr = CResult_____c_char Function(
ffi.Uint8 coin,
ffi.Uint32 id,
);
typedef _dart_get_t_addr = CResult_____c_char Function(
int coin,
int id,
);
typedef _c_get_sk = CResult_____c_char Function(
ffi.Uint8 coin,
ffi.Uint32 id,
);
typedef _dart_get_sk = CResult_____c_char Function(
int coin,
int id,
);
typedef _c_update_account_name = CResult_u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
ffi.Pointer<ffi.Int8> name,
);
typedef _dart_update_account_name = CResult_u8 Function(
int coin,
int id,
ffi.Pointer<ffi.Int8> name,
);
typedef _c_get_balances = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
ffi.Uint32 confirmed_height,
);
typedef _dart_get_balances = CResult______u8 Function(
int coin,
int id,
int confirmed_height,
);
typedef _c_get_db_height = CResult______u8 Function(
ffi.Uint8 coin,
);
typedef _dart_get_db_height = CResult______u8 Function(
int coin,
);
typedef _c_get_notes = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
);
typedef _dart_get_notes = CResult______u8 Function(
int coin,
int id,
);
typedef _c_get_txs = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
);
typedef _dart_get_txs = CResult______u8 Function(
int coin,
int id,
);
typedef _c_get_messages = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
);
typedef _dart_get_messages = CResult______u8 Function(
int coin,
int id,
);
typedef _c_get_prev_next_message = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
ffi.Pointer<ffi.Int8> subject,
ffi.Uint32 height,
);
typedef _dart_get_prev_next_message = CResult______u8 Function(
int coin,
int id,
ffi.Pointer<ffi.Int8> subject,
int height,
);
typedef _c_get_templates = CResult______u8 Function(
ffi.Uint8 coin,
);
typedef _dart_get_templates = CResult______u8 Function(
int coin,
);
typedef _c_save_send_template = CResult_u32 Function(
ffi.Uint8 coin,
ffi.Pointer<ffi.Int8> template_,
ffi.Pointer<ffi.Uint8> template_,
ffi.Uint64 len,
);
typedef _dart_save_send_template = CResult_u32 Function(
int coin,
ffi.Pointer<ffi.Int8> template_,
ffi.Pointer<ffi.Uint8> template_,
int len,
);
typedef _c_delete_send_template = CResult_u8 Function(
@ -1583,6 +2120,72 @@ typedef _dart_delete_send_template = CResult_u8 Function(
int id,
);
typedef _c_get_contacts = CResult______u8 Function(
ffi.Uint8 coin,
);
typedef _dart_get_contacts = CResult______u8 Function(
int coin,
);
typedef _c_get_pnl_txs = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
ffi.Uint32 timestamp,
);
typedef _dart_get_pnl_txs = CResult______u8 Function(
int coin,
int id,
int timestamp,
);
typedef _c_get_historical_prices = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 timestamp,
ffi.Pointer<ffi.Int8> currency,
);
typedef _dart_get_historical_prices = CResult______u8 Function(
int coin,
int timestamp,
ffi.Pointer<ffi.Int8> currency,
);
typedef _c_get_spendings = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
ffi.Uint32 timestamp,
);
typedef _dart_get_spendings = CResult______u8 Function(
int coin,
int id,
int timestamp,
);
typedef _c_update_excluded = CResult_u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
ffi.Int8 excluded,
);
typedef _dart_update_excluded = CResult_u8 Function(
int coin,
int id,
int excluded,
);
typedef _c_invert_excluded = CResult_u8 Function(
ffi.Uint8 coin,
ffi.Uint32 id,
);
typedef _dart_invert_excluded = CResult_u8 Function(
int coin,
int id,
);
typedef _c_has_cuda = ffi.Int8 Function();
typedef _dart_has_cuda = int Function();

View File

@ -197,6 +197,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flat_buffers:
dependency: "direct main"
description:
name: flat_buffers
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
flutter:
dependency: "direct main"
description: flutter

View File

@ -14,6 +14,7 @@ dependencies:
convert: ^3.0.1
protobuf: ^2.0.0
json_annotation: ^4.1.0
flat_buffers: ^2.0.0
dev_dependencies:
flutter_test:
@ -28,8 +29,8 @@ ffigen:
entry-points:
- '../../native/zcash-sync/binding.h'
# On MacOS
# llvm-path:
# - '/opt/homebrew/Cellar/llvm/15.0.6'
llvm-path:
- '/opt/homebrew/Cellar/llvm/15.0.6'
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -395,6 +395,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.50.1"
flat_buffers:
dependency: transitive
description:
name: flat_buffers
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
flex_color_scheme:
dependency: "direct main"
description:
@ -1238,34 +1245,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0"
sqflite:
dependency: "direct main"
description:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2+1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1+1"
sqflite_common_ffi:
dependency: "direct main"
description:
name: sqflite_common_ffi
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
sqlite3:
dependency: transitive
description:
name: sqlite3
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
stack_trace:
dependency: transitive
description:
@ -1294,13 +1273,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
synchronized:
dependency: transitive
description:
name: synchronized
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0+2"
term_glyph:
dependency: transitive
description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.2.17+336
version: 1.2.17+337
environment:
sdk: ">=2.12.0 <3.0.0"
@ -25,8 +25,6 @@ dependencies:
sdk: flutter
warp_api:
path: packages/warp_api_ffi
sqflite: ^2.0.2
sqflite_common_ffi: ^2.1.0
flutter_mobx: ^2.0.2
qr_flutter: ^4.0.0
http: ^0.13.3