BTC Testnet

This commit is contained in:
Hanh 2023-06-07 13:49:50 +10:00
parent 0b1f2447be
commit 2efa5dfa21
27 changed files with 1368 additions and 341 deletions

BIN
assets/bitcoin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1 +1 @@
FLUTTER_VERSION=3.10.2
FLUTTER_VERSION=3.10.3

View File

@ -47,7 +47,7 @@ table ShieldedTx {
short_tx_id:string;
timestamp:uint32;
name:string;
value:uint64;
value:int64;
address:string;
memo:string;
}
@ -56,6 +56,48 @@ table ShieldedTxVec {
txs:[ShieldedTx];
}
table PlainTx {
id:uint32;
tx_id:string;
height:uint32;
timestamp:uint32;
value:int64;
}
table PlainTxVec {
txs:[PlainTx];
}
table PlainNote {
id:uint32;
tx_id:string;
height:uint32;
timestamp:uint32;
vout:uint32;
value:uint64;
}
table PlainNoteVec {
notes:[PlainNote];
}
table BTCInput {
tx_id:string;
vout:uint32;
value:uint64;
}
table BTCOutput {
script_pubkey:string;
value:uint64;
}
table BTCTx {
txins:[BTCInput];
txouts:[BTCOutput];
fee:uint64;
}
table Message {
id_msg:uint32;
id_tx:uint32;

View File

@ -25,6 +25,11 @@ class _AccountState extends State<AccountPage>
@override
bool get wantKeepAlive => true; //Set to true
@override
void initState() {
if (!_hasAddress(active.addrMode)) active.addrMode = _getNextMode();
}
@override
Widget build(BuildContext context) {
super.build(context);
@ -182,7 +187,7 @@ class QRAddressState extends State<QRAddressWidget> {
final simpleMode = settings.simpleMode;
final address = _address();
final shortAddress = centerTrim(address);
final addrMode = active.addrMode;
var addrMode = active.addrMode;
final qrSize = getScreenSize(context) / 2.5;
final coinDef = active.coinDef;
final nextMode = _getNextMode();
@ -199,7 +204,7 @@ class QRAddressState extends State<QRAddressWidget> {
child: QrImage(
data: address,
size: qrSize,
embeddedImage: coinDef.image,
embeddedImage: AssetImage(coinDef.image),
backgroundColor: Colors.white))),
Padding(padding: EdgeInsets.symmetric(vertical: 8)),
RichText(
@ -338,24 +343,6 @@ class QRAddressState extends State<QRAddressWidget> {
}
}
int _getNextMode() {
int next = active.addrMode;
do {
next = (next + 1) % 3;
switch (next) {
case 0:
return next;
case 1: // we have orchard -> show zaddr
if (addrsAvailable & 4 != 0) return next;
break;
case 2: // we have taddr -> show taddr
if (addrsAvailable & 1 != 0) return next;
break;
}
} while (next != active.addrMode);
return 0; // unreachable
}
String? _getTapMessage(int mode) {
final s = S.of(context);
switch (mode) {
@ -519,3 +506,24 @@ _getBalanceHi(int b) => decimalFormat((b.abs() ~/ 100000) / 1000.0, 3);
_getBalanceLo(int b) => (b.abs() % 100000).toString().padLeft(5, '0');
_sign(int b) => b < 0 ? '-' : '+';
bool _hasAddress(int mode) {
switch (mode) {
case 0:
return active.availabeAddrs & 2 != 0;
case 1:
return active.availabeAddrs & 4 != 0;
case 2:
return active.availabeAddrs & 1 != 0;
}
return false;
}
int _getNextMode() {
int next = active.addrMode;
do {
next = (next + 1) % 3;
if (_hasAddress(next)) return next;
} while (next != active.addrMode);
return 0; // unreachable
}

View File

@ -80,15 +80,16 @@ class AccountManagerState extends State<AccountManagerPage> {
break;
}
final weight = a.active ? FontWeight.bold : FontWeight.normal;
final def = settings.coins[a.coin].def;
final zbal = a.balance / ZECUNIT;
final tbal = a.tbalance / ZECUNIT;
final tbal = def.coin < 2 ? a.tbalance / ZECUNIT : 0.0;
final balance = zbal + tbal;
return Card(
child: Dismissible(
key: Key(a.name),
child: ListTile(
leading: CircleAvatar(
backgroundImage: settings.coins[a.coin].def.image),
leading:
CircleAvatar(backgroundImage: AssetImage(def.image)),
title: RichText(
text: TextSpan(children: [
TextSpan(

View File

@ -162,6 +162,8 @@ abstract class _ActiveAccount with Store {
return AccountId(coin, id);
}
bool get isPrivate => coin < 2;
@action
Future<void> restore() async {
final prefs = await SharedPreferences.getInstance();
@ -234,7 +236,7 @@ abstract class _ActiveAccount with Store {
@action
void updateTBalance() {
try {
tbalance = WarpApi.getTBalance();
tbalance = WarpApi.getTBalance(active.coin, active.id);
} on String {}
}
@ -368,13 +370,15 @@ abstract class _ActiveAccount with Store {
@action
void excludeNote(Note note) {
WarpApi.updateExcluded(coin, note.id, note.excluded);
if (active.isPrivate) WarpApi.updateExcluded(coin, note.id, note.excluded);
}
@action
void invertExcludedNotes() {
WarpApi.invertExcluded(coin, id);
notes = notes.map((n) => n.invertExcluded).toList();
if (active.isPrivate) {
WarpApi.invertExcluded(coin, id);
notes = notes.map((n) => n.invertExcluded).toList();
}
}
@action

View File

@ -31,22 +31,26 @@ class BudgetState extends State<BudgetWidget>
final _ = active.dataEpoch;
return Column(
children: [
Card(
child: Column(children: [
Text(S.of(context).largestSpendingsByAddress,
style: Theme.of(context).textTheme.titleLarge),
Padding(padding: EdgeInsets.symmetric(vertical: 4)),
BudgetChart(),
])),
if (active.isPrivate)
Card(
child: Column(children: [
Text(S.of(context).largestSpendingsByAddress,
style: Theme.of(context).textTheme.titleLarge),
Padding(padding: EdgeInsets.symmetric(vertical: 4)),
BudgetChart(),
])),
Expanded(
child: Card(
child: Column(children: [
Text(S.of(context).accountBalanceHistory,
style: Theme.of(context).textTheme.titleLarge),
Padding(padding: EdgeInsets.symmetric(vertical: 4)),
Expanded(child: Padding(padding: EdgeInsets.only(right: 20),
child: LineChartTimeSeries.fromTimeSeries(active.accountBalances)))
]))),
Text(S.of(context).accountBalanceHistory,
style: Theme.of(context).textTheme.titleLarge),
Padding(padding: EdgeInsets.symmetric(vertical: 4)),
Expanded(
child: Padding(
padding: EdgeInsets.only(right: 20),
child: LineChartTimeSeries.fromTimeSeries(
active.accountBalances)))
]))),
],
);
}));
@ -67,27 +71,31 @@ class PnLState extends State<PnLWidget> with AutomaticKeepAliveClientMixin {
super.build(context);
return IconTheme.merge(
data: IconThemeData(opacity: 0.54),
child:
Column(children: [
child: Column(children: [
Row(children: [
Expanded(child:
FormBuilderRadioGroup(
orientation: OptionsOrientation.horizontal,
name: S.of(context).pnl,
initialValue: active.pnlSeriesIndex,
onChanged: (int? v) {
setState(() {
active.setPnlSeriesIndex(v!);
});
},
options: [
FormBuilderFieldOption(child: Text(S.of(context).price), value: 0),
Expanded(
child: FormBuilderRadioGroup(
orientation: OptionsOrientation.horizontal,
name: S.of(context).pnl,
initialValue: active.pnlSeriesIndex,
onChanged: (int? v) {
setState(() {
active.setPnlSeriesIndex(v!);
});
},
options: [
FormBuilderFieldOption(
child: Text(S.of(context).price), value: 0),
FormBuilderFieldOption(
child: Text(S.of(context).realized), value: 1),
FormBuilderFieldOption(child: Text(S.of(context).mm), value: 2),
FormBuilderFieldOption(child: Text(S.of(context).total), value: 3),
FormBuilderFieldOption(child: Text(S.of(context).qty), value: 4),
FormBuilderFieldOption(child: Text(S.of(context).table), value: 5),
FormBuilderFieldOption(
child: Text(S.of(context).mm), value: 2),
FormBuilderFieldOption(
child: Text(S.of(context).total), value: 3),
FormBuilderFieldOption(
child: Text(S.of(context).qty), value: 4),
FormBuilderFieldOption(
child: Text(S.of(context).table), value: 5),
])),
IconButton(onPressed: _onExport, icon: Icon(Icons.save)),
]),
@ -97,21 +105,23 @@ class PnLState extends State<PnLWidget> with AutomaticKeepAliveClientMixin {
child: Padding(
padding: EdgeInsets.only(right: 20),
child: active.pnlSeriesIndex != 5
? PnLChart(
active.pnls, active.pnlSeriesIndex)
? PnLChart(active.pnls, active.pnlSeriesIndex)
: PnLTable()));
})
]));
}
_onExport() async {
final csvData = active.pnlSorted.map((pnl) => [
pnl.timestamp,
pnl.amount,
pnl.price,
pnl.realized,
pnl.unrealized,
pnl.realized + pnl.unrealized]).toList();
final csvData = active.pnlSorted
.map((pnl) => [
pnl.timestamp,
pnl.amount,
pnl.price,
pnl.realized,
pnl.unrealized,
pnl.realized + pnl.unrealized
])
.toList();
await shareCsv(csvData, 'pnl_history.csv', S.of(context).pnlHistory);
}
}
@ -148,8 +158,8 @@ class PnLChart extends StatelessWidget {
List<PnL> data, int index, BuildContext context) {
return data
.map((pnl) => TimeSeriesPoint(
pnl.timestamp.millisecondsSinceEpoch ~/ DAY_MS,
_seriesData(pnl, index)))
pnl.timestamp.millisecondsSinceEpoch ~/ DAY_MS,
_seriesData(pnl, index)))
.toList();
}
}
@ -227,10 +237,13 @@ class BudgetChartState extends State<BudgetChart> {
return Observer(
builder: (context) => Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
HorizontalBarChart(active.spendings.map((s) => s.amount / ZECUNIT).toList()),
BudgetTable(active.spendings)
])));
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
HorizontalBarChart(
active.spendings.map((s) => s.amount / ZECUNIT).toList()),
BudgetTable(active.spendings)
])));
}
}
@ -241,17 +254,24 @@ class BudgetTable extends StatelessWidget {
@override
Widget build(BuildContext context) {
final palette = getPalette(Theme.of(context).primaryColor, spendings.length);
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 style = TextStyle(
color: palette[e.key], fontFeatures: [FontFeature.tabularFigures()]);
final recipient = e.value.recipient!;
return TableRow(children: [
Text(recipient, style: style, maxLines: 1, overflow: TextOverflow.ellipsis,),
Text(
recipient,
style: style,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(decimalFormat(e.value.amount / ZECUNIT, 8), style: style)
]);
}).toList();
return Table(
columnWidths: { 0: FlexColumnWidth(), 1: IntrinsicColumnWidth() },
children: rows);
columnWidths: {0: FlexColumnWidth(), 1: IntrinsicColumnWidth()},
children: rows);
}
}

23
lib/coin/btc.dart Normal file
View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import "coin.dart";
class BTCCoin extends CoinBase {
int coin = 2;
bool transparentOnly = true;
String name = "Bitcoin";
String symbol = "\u20BF";
String currency = "bitcoin";
int coinIndex = 0;
String ticker = "BTC";
String dbName = "btc.db";
String image = 'assets/bitcoin.png';
List<LWInstance> lwd = [
LWInstance("Blockstream", "tcp://blackie.c3-soft.com:57005")
];
bool supportsUA = false;
bool supportsMultisig = false;
List<double> weights = [0.001, 0.01, 0.1];
List<String> blockExplorers = ["https://blockstream.info/testnet/tx"];
bool supportsLedger = false;
}

View File

@ -16,12 +16,11 @@ class LWInstance {
abstract class CoinBase {
String get name;
int get coin;
String get app;
String get symbol;
String get currency;
String get ticker;
int get coinIndex;
AssetImage get image;
String get image;
String get dbName;
late String dbDir;
late String dbFullPath;

View File

@ -1,11 +1,10 @@
import 'coin.dart';
import 'ycash.dart';
import 'zcash.dart';
import 'zcashtest.dart';
import 'btc.dart';
CoinBase ycash = YcashCoin();
CoinBase zcash = ZcashCoin();
CoinBase zcashtest = ZcashTestCoin();
final coins = [zcash, ycash];
CoinBase btc = BTCCoin();
final coins = [zcash, ycash, btc];

View File

@ -5,13 +5,12 @@ import "coin.dart";
class YcashCoin extends CoinBase {
int coin = 1;
String name = "Ycash";
String app = "YWallet";
String symbol = "\u24E8";
String currency = "ycash";
int coinIndex = 347;
String ticker = "YEC";
String dbName = "yec.db";
AssetImage image = AssetImage('assets/ycash.png');
String image = 'assets/ycash.png';
List<LWInstance> lwd = [
LWInstance("Lightwalletd", "https://lite.ycash.xyz:9067"),
];

View File

@ -5,13 +5,12 @@ import 'coin.dart';
class ZcashCoin extends CoinBase {
int coin = 0;
String name = "Zcash";
String app = "ZWallet";
String symbol = "\u24E9";
String currency = "zcash";
int coinIndex = 133;
String ticker = "ZEC";
String dbName = "zec.db";
AssetImage image = AssetImage('assets/zcash.png');
String image = 'assets/zcash.png';
List<LWInstance> lwd = [
LWInstance("Lightwalletd", "https://mainnet.lightwalletd.com:9067"),
LWInstance("Zecwallet", "https://lwdv3.zecwallet.co"),

View File

@ -5,13 +5,12 @@ import 'coin.dart';
class ZcashTestCoin extends CoinBase {
int coin = 0;
String name = "Zcash Test";
String app = "ZWallet";
String symbol = "\u24E9";
String currency = "zcash";
int coinIndex = 133;
String ticker = "ZEC";
String dbName = "zec-test.db";
AssetImage image = AssetImage('assets/zcash.png');
String image = 'assets/zcash.png';
List<LWInstance> lwd = [
LWInstance("Lightwalletd", "https://testnet.lightwalletd.com:9067"),
];

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
@ -8,6 +9,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:velocity_x/velocity_x.dart';
import 'package:warp_api/data_fb_generated.dart';
import 'package:warp_api/warp_api.dart';
import 'package:badges/badges.dart' as Badges;
@ -112,13 +114,34 @@ class HomeInnerState extends State<HomeInnerPage>
TabController? _tabController;
int _tabIndex = 0;
final contactKey = GlobalKey<ContactsState>();
List<String> tabsShown = [];
@override
void initState() {
super.initState();
if (Platform.isAndroid) _initForegroundTask();
final tabController =
TabController(length: settings.simpleMode ? 4 : 7, vsync: this);
var tabs = {
// order is guaranteed by dart
"account": true,
"messages": true,
"notes": false,
"history": true,
"budget": false,
"pnl": false,
"contacts": true,
};
if (!settings.simpleMode) {
tabs["notes"] = true;
tabs["budget"] = true;
tabs["pnl"] = true;
}
if (!active.isPrivate) {
tabs["messages"] = false;
tabs["contacts"] = false;
}
tabsShown =
tabs.entries.where((kv) => kv.value).map((kv) => kv.key).toList();
final tabController = TabController(length: tabsShown.length, vsync: this);
tabController.addListener(() {
setState(() {
_tabIndex = tabController.index;
@ -133,7 +156,8 @@ class HomeInnerState extends State<HomeInnerPage>
final theme = Theme.of(context);
final simpleMode = settings.simpleMode;
final contactTabIndex = simpleMode ? 3 : 6;
final contactTabIndex = tabsShown.indexOf("contacts");
final messageTabIndex = tabsShown.indexOf("messages");
Widget button = Container();
if (_tabIndex == 0)
button = FloatingActionButton(
@ -147,7 +171,7 @@ class HomeInnerState extends State<HomeInnerPage>
backgroundColor: theme.colorScheme.secondary,
child: Icon(Icons.add),
);
else if (_tabIndex == 1)
else if (_tabIndex == messageTabIndex)
button = FloatingActionButton(
onPressed: _onSend,
backgroundColor: theme.colorScheme.secondary,
@ -184,13 +208,15 @@ class HomeInnerState extends State<HomeInnerPage>
return SizedBox(); // Show a placeholder
}
final privateCoin = active.isPrivate;
final menu = PopupMenuButton<String>(
itemBuilder: (context) {
return [
PopupMenuItem(child: Text(s.accounts), value: "Accounts"),
PopupMenuItem(child: Text(s.backup), value: "Backup"),
PopupMenuItem(child: Text(rescanMsg), value: "Rescan"),
if (!simpleMode)
if (!simpleMode && privateCoin)
PopupMenuItem(child: Text(s.pools), value: "Pools"),
if (!simpleMode)
PopupMenuItem(
@ -216,18 +242,18 @@ class HomeInnerState extends State<HomeInnerPage>
child: Text(s.broadcast), value: "Broadcast"),
PopupMenuItem(
child: Text(s.multipay), value: "MultiPay"),
PopupMenuItem(
child: Text(s.keyTool),
enabled: active.canPay,
value: "KeyTool"),
PopupMenuItem(
child: Text(s.sweep),
enabled: active.canPay,
value: "Sweep"),
if (privateCoin)
PopupMenuItem(
child: Text(s.keyTool),
enabled: active.canPay,
value: "KeyTool"),
if (privateCoin)
PopupMenuItem(
child: Text(s.sweep),
enabled: active.canPay,
value: "Sweep"),
],
onSelected: _onMenu)),
// if (!simpleMode && !isMobile())
// PopupMenuItem(child: Text(s.ledger), value: "Ledger"),
if (settings.isDeveloper)
PopupMenuItem(child: Text(s.expert), value: "Expert"),
PopupMenuItem(child: Text(s.settings), value: "Settings"),
@ -252,12 +278,12 @@ class HomeInnerState extends State<HomeInnerPage>
isScrollable: true,
tabs: [
Tab(text: s.account),
messageTab,
if (!simpleMode) Tab(text: s.notes),
Tab(text: s.history),
if (!simpleMode) Tab(text: s.budget),
if (!simpleMode) Tab(text: s.tradingPl),
Tab(text: s.contacts),
if (tabsShown.contains("messages")) messageTab,
if (tabsShown.contains("notes")) Tab(text: s.notes),
if (tabsShown.contains("history")) Tab(text: s.history),
if (tabsShown.contains("budget")) Tab(text: s.budget),
if (tabsShown.contains("pnl")) Tab(text: s.tradingPl),
if (tabsShown.contains("contacts")) Tab(text: s.contacts),
],
),
actions: [menu],
@ -266,12 +292,12 @@ class HomeInnerState extends State<HomeInnerPage>
controller: _tabController,
children: [
AccountPage(),
MessageWidget(messageKey),
if (!simpleMode) NoteWidget(),
HistoryWidget(),
if (!simpleMode) BudgetWidget(),
if (!simpleMode) PnLWidget(),
ContactsTab(key: contactKey),
if (tabsShown.contains("messages")) MessageWidget(messageKey),
if (tabsShown.contains("notes")) NoteWidget(),
if (tabsShown.contains("history")) HistoryWidget(),
if (tabsShown.contains("budget")) BudgetWidget(),
if (tabsShown.contains("pnl")) PnLWidget(),
if (tabsShown.contains("contacts")) ContactsTab(key: contactKey),
],
),
floatingActionButton: button,
@ -492,7 +518,7 @@ class HomeInnerState extends State<HomeInnerPage>
if (rawTx != null) {
try {
final res = WarpApi.broadcast(rawTx);
final res = WarpApi.broadcast(active.coin, rawTx);
showSnackBar(res);
} on String catch (e) {
showSnackBar(e, error: true);

View File

@ -12,12 +12,17 @@ class NoteWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Observer(builder: (context) {
switch (settings.noteView) {
case ViewStyle.Table: return NoteTable();
case ViewStyle.List: return NoteList();
case ViewStyle.Auto: return OrientationBuilder(builder: (context, orientation) {
if (orientation == Orientation.portrait) return NoteList();
else return NoteTable();
});
case ViewStyle.Table:
return NoteTable();
case ViewStyle.List:
return NoteList();
case ViewStyle.Auto:
return OrientationBuilder(builder: (context, orientation) {
if (orientation == Orientation.portrait)
return NoteList();
else
return NoteTable();
});
}
});
}
@ -30,7 +35,8 @@ class NoteTable extends StatefulWidget {
State<StatefulWidget> createState() => _NoteTableState();
}
class _NoteTableState extends State<NoteTable> with AutomaticKeepAliveClientMixin {
class _NoteTableState extends State<NoteTable>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true; //Set to true
@ -77,7 +83,9 @@ class _NoteTableState extends State<NoteTable> with AutomaticKeepAliveClientMixi
header: Text(S.of(context).selectNotesToExcludeFromPayments,
style: Theme.of(context).textTheme.bodyMedium),
actions: [
IconButton(onPressed: _selectInverse, icon: Icon(MdiIcons.selectInverse)),
IconButton(
onPressed: _selectInverse,
icon: Icon(MdiIcons.selectInverse)),
],
columnSpacing: 16,
showCheckboxColumn: false,
@ -93,6 +101,7 @@ class _NoteTableState extends State<NoteTable> with AutomaticKeepAliveClientMixi
}
_onRowSelected(Note note) {
if (!active.isPrivate) return;
active.excludeNote(note);
}
@ -121,8 +130,7 @@ class NotesDataSource extends DataTableSource {
if (note.spent)
style = style.merge(TextStyle(decoration: TextDecoration.lineThrough));
if (note.orchard)
style = style.merge(TextStyle(color: theme.primaryColor));
if (note.orchard) style = style.merge(TextStyle(color: theme.primaryColor));
final amountStyle = weightFromAmount(style, note.value);
@ -135,7 +143,8 @@ class NotesDataSource extends DataTableSource {
: theme.colorScheme.background),
cells: [
DataCell(Text("$confsOrHeight", style: style)),
DataCell(Text("${noteDateFormat.format(note.timestamp)}", style: style)),
DataCell(
Text("${noteDateFormat.format(note.timestamp)}", style: style)),
DataCell(Text(decimalFormat(note.value, 8), style: amountStyle)),
],
onSelectChanged: (selected) => _noteSelected(note, selected),
@ -152,6 +161,7 @@ class NotesDataSource extends DataTableSource {
int get selectedRowCount => 0;
void _noteSelected(Note note, bool? selected) {
if (!active.isPrivate) return;
note.excluded = !note.excluded;
notifyListeners();
onRowSelected(note);
@ -170,21 +180,24 @@ class NoteListState extends State<NoteList> with AutomaticKeepAliveClientMixin {
final s = S.of(context);
return Observer(builder: (context) {
final notes = active.sortedNotes;
return Padding(padding: EdgeInsets.all(16), child: CustomScrollView(
key: UniqueKey(),
slivers: [
SliverToBoxAdapter(child: ListTile(
onTap: _onInvert,
title: Text(s.selectNotesToExcludeFromPayments),
trailing: Icon(Icons.select_all),
)),
SliverFixedExtentList(
itemExtent: 50,
delegate: SliverChildBuilderDelegate((context, index) {
return NoteItem(notes[index], index);
}, childCount: notes.length))
],
));
return Padding(
padding: EdgeInsets.all(16),
child: CustomScrollView(
key: UniqueKey(),
slivers: [
SliverToBoxAdapter(
child: ListTile(
onTap: _onInvert,
title: Text(s.selectNotesToExcludeFromPayments),
trailing: Icon(Icons.select_all),
)),
SliverFixedExtentList(
itemExtent: 50,
delegate: SliverChildBuilderDelegate((context, index) {
return NoteItem(notes[index], index);
}, childCount: notes.length))
],
));
});
}
@ -225,24 +238,33 @@ class NoteItemState extends State<NoteItem> {
style = style.merge(TextStyle(color: style.color!.withOpacity(0.5)));
if (note.spent)
style = style.merge(TextStyle(decoration: TextDecoration.lineThrough));
if (note.orchard)
style = style.merge(TextStyle(color: theme.primaryColor));
if (note.orchard) style = style.merge(TextStyle(color: theme.primaryColor));
final amountStyle = weightFromAmount(style, note.value);
return GestureDetector(onTap: _onSelected, behavior: HitTestBehavior.opaque, child:
ColoredBox(color: excluded ? theme.primaryColor.withOpacity(0.5) : theme.colorScheme.background, child:
Padding(padding: EdgeInsets.all(8), child:
Row(children: [
Column(children: [Text("${note.height}", style: theme.textTheme.bodySmall),
Text("$confirmations", style: theme.textTheme.bodyMedium),
]),
Expanded(child: Center(child: Text("${note.value}", style: amountStyle))),
Text("$timestamp"),
]))));
return GestureDetector(
onTap: _onSelected,
behavior: HitTestBehavior.opaque,
child: ColoredBox(
color: excluded
? theme.primaryColor.withOpacity(0.5)
: theme.colorScheme.background,
child: Padding(
padding: EdgeInsets.all(8),
child: Row(children: [
Column(children: [
Text("${note.height}", style: theme.textTheme.bodySmall),
Text("$confirmations", style: theme.textTheme.bodyMedium),
]),
Expanded(
child: Center(
child: Text("${note.value}", style: amountStyle))),
Text("$timestamp"),
]))));
}
_onSelected() {
if (!active.isPrivate) return;
setState(() {
excluded = !excluded;
widget.note.excluded = excluded;
@ -254,4 +276,3 @@ class NoteItemState extends State<NoteItem> {
bool confirmed(int height) {
return syncStatus.latestHeight - height >= settings.anchorOffset;
}

View File

@ -9,6 +9,7 @@ final rescanKey = GlobalKey<RescanFormState>();
Future<int?> rescanDialog(BuildContext context) async {
try {
if (active.coin >= 2) return 0;
DateTime minDate = WarpApi.getActivationDate();
final bool approved = await showDialog<bool>(
context: context,

View File

@ -36,6 +36,14 @@ class _AddAccountPageState extends State<AddAccountPage> {
@override
Widget build(BuildContext context) {
final s = S.of(context);
final options = coins
.map((c) => FormBuilderFieldOption(
child: ListTile(
title: Text(c.name),
trailing: Image.asset(c.image, height: 32)),
value: c.coin))
.toList();
return Scaffold(
appBar: AppBar(
title: Text(s.newAccount),
@ -70,22 +78,7 @@ class _AddAccountPageState extends State<AddAccountPage> {
_coin = v!;
});
},
options: [
FormBuilderFieldOption(
child: ListTile(
title: Text('Ycash'),
trailing: Image.asset(
'assets/ycash.png',
height: 32)),
value: 1),
FormBuilderFieldOption(
child: ListTile(
title: Text('Zcash'),
trailing: Image.asset(
'assets/zcash.png',
height: 32)),
value: 0),
]),
options: options),
FormBuilderCheckbox(
name: 'restore',
title: Text(s.restoreAnAccount),

View File

@ -101,8 +101,10 @@ class SendState extends State<SendPage> {
_setPaymentURI(uri);
});
final templateIds = active.dbReader.loadTemplates();
_templates = templateIds;
if (active.isPrivate) {
final templateIds = active.dbReader.loadTemplates();
_templates = templateIds;
}
}
@override
@ -214,64 +216,67 @@ class SendState extends State<SendPage> {
if (!simpleMode)
BalanceTable(_sBalance, _tBalance, _excludedBalance,
_underConfirmedBalance, change, _usedBalance, _fee),
Container(
child: InputDecorator(
decoration: InputDecoration(labelText: s.memo),
child: Column(children: [
FormBuilderCheckbox(
key: UniqueKey(),
name: 'reply-to',
title: Text(s.includeReplyTo),
initialValue: _replyTo,
if (active.isPrivate)
Container(
child: InputDecorator(
decoration: InputDecoration(labelText: s.memo),
child: Column(children: [
FormBuilderCheckbox(
key: UniqueKey(),
name: 'reply-to',
title: Text(s.includeReplyTo),
initialValue: _replyTo,
onChanged: (v) {
setState(() {
_replyTo = v ?? false;
});
},
),
TextFormField(
decoration:
InputDecoration(labelText: s.subject),
controller: _subjectController,
),
TextFormField(
decoration:
InputDecoration(labelText: s.body),
minLines: 4,
maxLines: null,
keyboardType: TextInputType.multiline,
controller: _memoController,
)
]))),
Padding(padding: EdgeInsets.all(8)),
if (active.isPrivate)
Row(children: [
Expanded(
child: DropdownButtonFormField<SendTemplateT>(
hint: Text(s.template),
items: templates,
value: _template,
onChanged: (v) {
setState(() {
_replyTo = v ?? false;
_template = v;
});
},
),
TextFormField(
decoration:
InputDecoration(labelText: s.subject),
controller: _subjectController,
),
TextFormField(
decoration:
InputDecoration(labelText: s.body),
minLines: 4,
maxLines: null,
keyboardType: TextInputType.multiline,
controller: _memoController,
)
]))),
Padding(padding: EdgeInsets.all(8)),
Row(children: [
Expanded(
child: DropdownButtonFormField<SendTemplateT>(
hint: Text(s.template),
items: templates,
value: _template,
onChanged: (v) {
setState(() {
_template = v;
});
})),
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 ? _deleteTemplate : null,
icon: Icon(Icons.delete)),
]),
})),
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 ? _deleteTemplate : null,
icon: Icon(Icons.delete)),
]),
Padding(padding: EdgeInsets.all(8)),
ButtonBar(children: [
ElevatedButton.icon(

View File

@ -8,6 +8,7 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';
import 'package:velocity_x/velocity_x.dart';
import 'package:queue/queue.dart';
import 'package:shared_preferences_android/shared_preferences_android.dart';
import 'package:shared_preferences_ios/shared_preferences_ios.dart';
@ -21,6 +22,7 @@ import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:sensors_plus/sensors_plus.dart';
import 'coin/coin.dart';
import 'coin/coins.dart' as Coins;
import 'generated/l10n.dart';
import 'main.dart';
@ -92,7 +94,10 @@ abstract class _Settings with Store {
@observable
bool simpleMode = true;
List<CoinData> coins = [CoinData(0, zcash), CoinData(1, ycash)];
List<CoinData> coins = Coins.coins
.sortedByNum((c) => c.coin)
.map((c) => CoinData(c.coin, c))
.toList();
@observable
String version = "1.0.0";
@ -636,8 +641,8 @@ abstract class _PriceStore with Store {
if (f ||
_lastChartUpdateTime == null ||
now > _lastChartUpdateTime + 5 * 60) {
await fetchQueue
.add(() => WarpApi.syncHistoricalPrices(settings.currency));
await fetchQueue.add(
() => WarpApi.syncHistoricalPrices(active.coin, settings.currency));
active.fetchChartData();
lastChartUpdateTime = now;
}
@ -739,7 +744,7 @@ abstract class _SyncStatus with Store {
if (url.isNotEmpty) WarpApi.updateLWD(active.coin, url);
}
try {
latestHeight = await WarpApi.getLatestHeight();
latestHeight = await WarpApi.getLatestHeight(active.coin);
} on String {}
final _syncedInfo = getDbSyncedHeight();
// if syncedHeight = 0, we just started sync therefore don't set it back to null
@ -816,7 +821,7 @@ abstract class _SyncStatus with Store {
showSnackBar(S.current.rescanRequested(height));
syncedHeight = height;
timestamp = null;
WarpApi.rescanFrom(height);
WarpApi.rescanFrom(active.coin, height);
await sync(true);
final rh = pendingRescanHeight;
if (rh != null) {

View File

@ -50,17 +50,20 @@ class TransactionState extends State<TransactionPage> {
ListTile(
title: Text(S.of(context).amount),
subtitle: SelectableText(decimalFormat(tx.value, 8))),
ListTile(
title: Text(S.of(context).address),
subtitle: SelectableText('${tx.address}'),
trailing: IconButton(
icon: Icon(Icons.contacts), onPressed: _addContact)),
ListTile(
title: Text(S.of(context).contactName),
subtitle: SelectableText('${tx.contact ?? "N/A"}')),
ListTile(
title: Text(S.of(context).memo),
subtitle: SelectableText('${tx.memo}')),
if (active.isPrivate)
ListTile(
title: Text(S.of(context).address),
subtitle: SelectableText('${tx.address}'),
trailing: IconButton(
icon: Icon(Icons.contacts), onPressed: _addContact)),
if (active.isPrivate)
ListTile(
title: Text(S.of(context).contactName),
subtitle: SelectableText('${tx.contact ?? "N/A"}')),
if (active.isPrivate)
ListTile(
title: Text(S.of(context).memo),
subtitle: SelectableText('${tx.memo}')),
ButtonBar(alignment: MainAxisAlignment.center, children: [
IconButton(
onPressed: txIndex > 0 ? _prev : null,

View File

@ -31,10 +31,12 @@ class TxPlanPage extends StatelessWidget {
.map((e) => DataRow(cells: [
DataCell(Text('...${trailing(e.address!, 12)}')),
DataCell(Text('${poolToString(e.pool)}')),
DataCell(Text('${amountToString(e.amount, 3)}')),
DataCell(Text('${amountToString(e.amount, MAX_PRECISION)}')),
]))
.toList();
final invalidPrivacy = report.privacyLevel < settings.minPrivacyLevel;
// TODO: Abstract sat into coinDef
final feeFx = decimalFormat(report.fee * priceStore.coinPrice / ZECUNIT, 2);
return Scaffold(
appBar: AppBar(title: Text('Transaction Plan')),
@ -60,20 +62,22 @@ class TxPlanPage extends StatelessWidget {
title: Text('Transparent Input'),
trailing:
Text(amountToString(report.transparent, MAX_PRECISION))),
ListTile(
title: Text('Sapling Input'),
trailing:
Text(amountToString(report.sapling, MAX_PRECISION))),
if (supportsUA)
if (active.isPrivate)
ListTile(
title: Text('Sapling Input'),
trailing:
Text(amountToString(report.sapling, MAX_PRECISION))),
if (active.isPrivate && supportsUA)
ListTile(
title: Text('Orchard Input'),
trailing:
Text(amountToString(report.orchard, MAX_PRECISION))),
ListTile(
title: Text('Net Sapling Change'),
trailing:
Text(amountToString(report.netSapling, MAX_PRECISION))),
if (supportsUA)
if (active.isPrivate)
ListTile(
title: Text('Net Sapling Change'),
trailing:
Text(amountToString(report.netSapling, MAX_PRECISION))),
if (active.isPrivate && supportsUA)
ListTile(
title: Text('Net Orchard Change'),
trailing:
@ -81,6 +85,9 @@ class TxPlanPage extends StatelessWidget {
ListTile(
title: Text('Fee'),
trailing: Text(amountToString(report.fee, MAX_PRECISION))),
ListTile(
title: Text('Fee ${settings.currency}'),
trailing: Text(feeFx)),
privacyToString(context, report.privacyLevel)!,
if (invalidPrivacy)
Padding(

@ -1 +1 @@
Subproject commit 388e3de26c0a6a5d899024a406510285aee28736
Subproject commit ad2515d47ae1ba4a97cb9860170f4fa9228ffcaa

View File

@ -833,7 +833,7 @@ class ShieldedTx {
String? get shortTxId => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 10);
int get timestamp => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 12, 0);
String? get name => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 14);
int get value => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 16, 0);
int get value => const fb.Int64Reader().vTableGet(_bc, _bcOffset, 16, 0);
String? get address => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 18);
String? get memo => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 20);
@ -900,7 +900,7 @@ class ShieldedTxT implements fb.Packable {
fbBuilder.addOffset(3, shortTxIdOffset);
fbBuilder.addUint32(4, timestamp);
fbBuilder.addOffset(5, nameOffset);
fbBuilder.addUint64(6, value);
fbBuilder.addInt64(6, value);
fbBuilder.addOffset(7, addressOffset);
fbBuilder.addOffset(8, memoOffset);
return fbBuilder.endTable();
@ -954,7 +954,7 @@ class ShieldedTxBuilder {
return fbBuilder.offset;
}
int addValue(int? value) {
fbBuilder.addUint64(6, value);
fbBuilder.addInt64(6, value);
return fbBuilder.offset;
}
int addAddressOffset(int? offset) {
@ -1023,7 +1023,7 @@ class ShieldedTxObjectBuilder extends fb.ObjectBuilder {
fbBuilder.addOffset(3, shortTxIdOffset);
fbBuilder.addUint32(4, _timestamp);
fbBuilder.addOffset(5, nameOffset);
fbBuilder.addUint64(6, _value);
fbBuilder.addInt64(6, _value);
fbBuilder.addOffset(7, addressOffset);
fbBuilder.addOffset(8, memoOffset);
return fbBuilder.endTable();
@ -1139,6 +1139,906 @@ class ShieldedTxVecObjectBuilder extends fb.ObjectBuilder {
return fbBuilder.buffer;
}
}
class PlainTx {
PlainTx._(this._bc, this._bcOffset);
factory PlainTx(List<int> bytes) {
final rootRef = fb.BufferContext.fromBytes(bytes);
return reader.read(rootRef, 0);
}
static const fb.Reader<PlainTx> reader = _PlainTxReader();
final fb.BufferContext _bc;
final int _bcOffset;
int get id => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 4, 0);
String? get txId => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 6);
int get height => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 8, 0);
int get timestamp => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 10, 0);
int get value => const fb.Int64Reader().vTableGet(_bc, _bcOffset, 12, 0);
@override
String toString() {
return 'PlainTx{id: ${id}, txId: ${txId}, height: ${height}, timestamp: ${timestamp}, value: ${value}}';
}
PlainTxT unpack() => PlainTxT(
id: id,
txId: txId,
height: height,
timestamp: timestamp,
value: value);
static int pack(fb.Builder fbBuilder, PlainTxT? object) {
if (object == null) return 0;
return object.pack(fbBuilder);
}
}
class PlainTxT implements fb.Packable {
int id;
String? txId;
int height;
int timestamp;
int value;
PlainTxT({
this.id = 0,
this.txId,
this.height = 0,
this.timestamp = 0,
this.value = 0});
@override
int pack(fb.Builder fbBuilder) {
final int? txIdOffset = txId == null ? null
: fbBuilder.writeString(txId!);
fbBuilder.startTable(5);
fbBuilder.addUint32(0, id);
fbBuilder.addOffset(1, txIdOffset);
fbBuilder.addUint32(2, height);
fbBuilder.addUint32(3, timestamp);
fbBuilder.addInt64(4, value);
return fbBuilder.endTable();
}
@override
String toString() {
return 'PlainTxT{id: ${id}, txId: ${txId}, height: ${height}, timestamp: ${timestamp}, value: ${value}}';
}
}
class _PlainTxReader extends fb.TableReader<PlainTx> {
const _PlainTxReader();
@override
PlainTx createObject(fb.BufferContext bc, int offset) =>
PlainTx._(bc, offset);
}
class PlainTxBuilder {
PlainTxBuilder(this.fbBuilder);
final fb.Builder fbBuilder;
void begin() {
fbBuilder.startTable(5);
}
int addId(int? id) {
fbBuilder.addUint32(0, id);
return fbBuilder.offset;
}
int addTxIdOffset(int? offset) {
fbBuilder.addOffset(1, offset);
return fbBuilder.offset;
}
int addHeight(int? height) {
fbBuilder.addUint32(2, height);
return fbBuilder.offset;
}
int addTimestamp(int? timestamp) {
fbBuilder.addUint32(3, timestamp);
return fbBuilder.offset;
}
int addValue(int? value) {
fbBuilder.addInt64(4, value);
return fbBuilder.offset;
}
int finish() {
return fbBuilder.endTable();
}
}
class PlainTxObjectBuilder extends fb.ObjectBuilder {
final int? _id;
final String? _txId;
final int? _height;
final int? _timestamp;
final int? _value;
PlainTxObjectBuilder({
int? id,
String? txId,
int? height,
int? timestamp,
int? value,
})
: _id = id,
_txId = txId,
_height = height,
_timestamp = timestamp,
_value = value;
/// Finish building, and store into the [fbBuilder].
@override
int finish(fb.Builder fbBuilder) {
final int? txIdOffset = _txId == null ? null
: fbBuilder.writeString(_txId!);
fbBuilder.startTable(5);
fbBuilder.addUint32(0, _id);
fbBuilder.addOffset(1, txIdOffset);
fbBuilder.addUint32(2, _height);
fbBuilder.addUint32(3, _timestamp);
fbBuilder.addInt64(4, _value);
return fbBuilder.endTable();
}
/// Convenience method to serialize to byte list.
@override
Uint8List toBytes([String? fileIdentifier]) {
final fbBuilder = fb.Builder(deduplicateTables: false);
fbBuilder.finish(finish(fbBuilder), fileIdentifier);
return fbBuilder.buffer;
}
}
class PlainTxVec {
PlainTxVec._(this._bc, this._bcOffset);
factory PlainTxVec(List<int> bytes) {
final rootRef = fb.BufferContext.fromBytes(bytes);
return reader.read(rootRef, 0);
}
static const fb.Reader<PlainTxVec> reader = _PlainTxVecReader();
final fb.BufferContext _bc;
final int _bcOffset;
List<PlainTx>? get txs => const fb.ListReader<PlainTx>(PlainTx.reader).vTableGetNullable(_bc, _bcOffset, 4);
@override
String toString() {
return 'PlainTxVec{txs: ${txs}}';
}
PlainTxVecT unpack() => PlainTxVecT(
txs: txs?.map((e) => e.unpack()).toList());
static int pack(fb.Builder fbBuilder, PlainTxVecT? object) {
if (object == null) return 0;
return object.pack(fbBuilder);
}
}
class PlainTxVecT implements fb.Packable {
List<PlainTxT>? txs;
PlainTxVecT({
this.txs});
@override
int pack(fb.Builder fbBuilder) {
final int? txsOffset = txs == null ? null
: fbBuilder.writeList(txs!.map((b) => b.pack(fbBuilder)).toList());
fbBuilder.startTable(1);
fbBuilder.addOffset(0, txsOffset);
return fbBuilder.endTable();
}
@override
String toString() {
return 'PlainTxVecT{txs: ${txs}}';
}
}
class _PlainTxVecReader extends fb.TableReader<PlainTxVec> {
const _PlainTxVecReader();
@override
PlainTxVec createObject(fb.BufferContext bc, int offset) =>
PlainTxVec._(bc, offset);
}
class PlainTxVecBuilder {
PlainTxVecBuilder(this.fbBuilder);
final fb.Builder fbBuilder;
void begin() {
fbBuilder.startTable(1);
}
int addTxsOffset(int? offset) {
fbBuilder.addOffset(0, offset);
return fbBuilder.offset;
}
int finish() {
return fbBuilder.endTable();
}
}
class PlainTxVecObjectBuilder extends fb.ObjectBuilder {
final List<PlainTxObjectBuilder>? _txs;
PlainTxVecObjectBuilder({
List<PlainTxObjectBuilder>? txs,
})
: _txs = txs;
/// Finish building, and store into the [fbBuilder].
@override
int finish(fb.Builder fbBuilder) {
final int? txsOffset = _txs == null ? null
: fbBuilder.writeList(_txs!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
fbBuilder.startTable(1);
fbBuilder.addOffset(0, txsOffset);
return fbBuilder.endTable();
}
/// Convenience method to serialize to byte list.
@override
Uint8List toBytes([String? fileIdentifier]) {
final fbBuilder = fb.Builder(deduplicateTables: false);
fbBuilder.finish(finish(fbBuilder), fileIdentifier);
return fbBuilder.buffer;
}
}
class PlainNote {
PlainNote._(this._bc, this._bcOffset);
factory PlainNote(List<int> bytes) {
final rootRef = fb.BufferContext.fromBytes(bytes);
return reader.read(rootRef, 0);
}
static const fb.Reader<PlainNote> reader = _PlainNoteReader();
final fb.BufferContext _bc;
final int _bcOffset;
int get id => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 4, 0);
String? get txId => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 6);
int get height => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 8, 0);
int get timestamp => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 10, 0);
int get vout => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 12, 0);
int get value => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 14, 0);
@override
String toString() {
return 'PlainNote{id: ${id}, txId: ${txId}, height: ${height}, timestamp: ${timestamp}, vout: ${vout}, value: ${value}}';
}
PlainNoteT unpack() => PlainNoteT(
id: id,
txId: txId,
height: height,
timestamp: timestamp,
vout: vout,
value: value);
static int pack(fb.Builder fbBuilder, PlainNoteT? object) {
if (object == null) return 0;
return object.pack(fbBuilder);
}
}
class PlainNoteT implements fb.Packable {
int id;
String? txId;
int height;
int timestamp;
int vout;
int value;
PlainNoteT({
this.id = 0,
this.txId,
this.height = 0,
this.timestamp = 0,
this.vout = 0,
this.value = 0});
@override
int pack(fb.Builder fbBuilder) {
final int? txIdOffset = txId == null ? null
: fbBuilder.writeString(txId!);
fbBuilder.startTable(6);
fbBuilder.addUint32(0, id);
fbBuilder.addOffset(1, txIdOffset);
fbBuilder.addUint32(2, height);
fbBuilder.addUint32(3, timestamp);
fbBuilder.addUint32(4, vout);
fbBuilder.addUint64(5, value);
return fbBuilder.endTable();
}
@override
String toString() {
return 'PlainNoteT{id: ${id}, txId: ${txId}, height: ${height}, timestamp: ${timestamp}, vout: ${vout}, value: ${value}}';
}
}
class _PlainNoteReader extends fb.TableReader<PlainNote> {
const _PlainNoteReader();
@override
PlainNote createObject(fb.BufferContext bc, int offset) =>
PlainNote._(bc, offset);
}
class PlainNoteBuilder {
PlainNoteBuilder(this.fbBuilder);
final fb.Builder fbBuilder;
void begin() {
fbBuilder.startTable(6);
}
int addId(int? id) {
fbBuilder.addUint32(0, id);
return fbBuilder.offset;
}
int addTxIdOffset(int? offset) {
fbBuilder.addOffset(1, offset);
return fbBuilder.offset;
}
int addHeight(int? height) {
fbBuilder.addUint32(2, height);
return fbBuilder.offset;
}
int addTimestamp(int? timestamp) {
fbBuilder.addUint32(3, timestamp);
return fbBuilder.offset;
}
int addVout(int? vout) {
fbBuilder.addUint32(4, vout);
return fbBuilder.offset;
}
int addValue(int? value) {
fbBuilder.addUint64(5, value);
return fbBuilder.offset;
}
int finish() {
return fbBuilder.endTable();
}
}
class PlainNoteObjectBuilder extends fb.ObjectBuilder {
final int? _id;
final String? _txId;
final int? _height;
final int? _timestamp;
final int? _vout;
final int? _value;
PlainNoteObjectBuilder({
int? id,
String? txId,
int? height,
int? timestamp,
int? vout,
int? value,
})
: _id = id,
_txId = txId,
_height = height,
_timestamp = timestamp,
_vout = vout,
_value = value;
/// Finish building, and store into the [fbBuilder].
@override
int finish(fb.Builder fbBuilder) {
final int? txIdOffset = _txId == null ? null
: fbBuilder.writeString(_txId!);
fbBuilder.startTable(6);
fbBuilder.addUint32(0, _id);
fbBuilder.addOffset(1, txIdOffset);
fbBuilder.addUint32(2, _height);
fbBuilder.addUint32(3, _timestamp);
fbBuilder.addUint32(4, _vout);
fbBuilder.addUint64(5, _value);
return fbBuilder.endTable();
}
/// Convenience method to serialize to byte list.
@override
Uint8List toBytes([String? fileIdentifier]) {
final fbBuilder = fb.Builder(deduplicateTables: false);
fbBuilder.finish(finish(fbBuilder), fileIdentifier);
return fbBuilder.buffer;
}
}
class PlainNoteVec {
PlainNoteVec._(this._bc, this._bcOffset);
factory PlainNoteVec(List<int> bytes) {
final rootRef = fb.BufferContext.fromBytes(bytes);
return reader.read(rootRef, 0);
}
static const fb.Reader<PlainNoteVec> reader = _PlainNoteVecReader();
final fb.BufferContext _bc;
final int _bcOffset;
List<PlainNote>? get notes => const fb.ListReader<PlainNote>(PlainNote.reader).vTableGetNullable(_bc, _bcOffset, 4);
@override
String toString() {
return 'PlainNoteVec{notes: ${notes}}';
}
PlainNoteVecT unpack() => PlainNoteVecT(
notes: notes?.map((e) => e.unpack()).toList());
static int pack(fb.Builder fbBuilder, PlainNoteVecT? object) {
if (object == null) return 0;
return object.pack(fbBuilder);
}
}
class PlainNoteVecT implements fb.Packable {
List<PlainNoteT>? notes;
PlainNoteVecT({
this.notes});
@override
int pack(fb.Builder fbBuilder) {
final int? notesOffset = notes == null ? null
: fbBuilder.writeList(notes!.map((b) => b.pack(fbBuilder)).toList());
fbBuilder.startTable(1);
fbBuilder.addOffset(0, notesOffset);
return fbBuilder.endTable();
}
@override
String toString() {
return 'PlainNoteVecT{notes: ${notes}}';
}
}
class _PlainNoteVecReader extends fb.TableReader<PlainNoteVec> {
const _PlainNoteVecReader();
@override
PlainNoteVec createObject(fb.BufferContext bc, int offset) =>
PlainNoteVec._(bc, offset);
}
class PlainNoteVecBuilder {
PlainNoteVecBuilder(this.fbBuilder);
final fb.Builder fbBuilder;
void begin() {
fbBuilder.startTable(1);
}
int addNotesOffset(int? offset) {
fbBuilder.addOffset(0, offset);
return fbBuilder.offset;
}
int finish() {
return fbBuilder.endTable();
}
}
class PlainNoteVecObjectBuilder extends fb.ObjectBuilder {
final List<PlainNoteObjectBuilder>? _notes;
PlainNoteVecObjectBuilder({
List<PlainNoteObjectBuilder>? notes,
})
: _notes = notes;
/// Finish building, and store into the [fbBuilder].
@override
int finish(fb.Builder fbBuilder) {
final int? notesOffset = _notes == null ? null
: fbBuilder.writeList(_notes!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
fbBuilder.startTable(1);
fbBuilder.addOffset(0, notesOffset);
return fbBuilder.endTable();
}
/// Convenience method to serialize to byte list.
@override
Uint8List toBytes([String? fileIdentifier]) {
final fbBuilder = fb.Builder(deduplicateTables: false);
fbBuilder.finish(finish(fbBuilder), fileIdentifier);
return fbBuilder.buffer;
}
}
class Btcinput {
Btcinput._(this._bc, this._bcOffset);
factory Btcinput(List<int> bytes) {
final rootRef = fb.BufferContext.fromBytes(bytes);
return reader.read(rootRef, 0);
}
static const fb.Reader<Btcinput> reader = _BtcinputReader();
final fb.BufferContext _bc;
final int _bcOffset;
String? get txId => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 4);
int get vout => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 6, 0);
int get value => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 8, 0);
@override
String toString() {
return 'Btcinput{txId: ${txId}, vout: ${vout}, value: ${value}}';
}
BtcinputT unpack() => BtcinputT(
txId: txId,
vout: vout,
value: value);
static int pack(fb.Builder fbBuilder, BtcinputT? object) {
if (object == null) return 0;
return object.pack(fbBuilder);
}
}
class BtcinputT implements fb.Packable {
String? txId;
int vout;
int value;
BtcinputT({
this.txId,
this.vout = 0,
this.value = 0});
@override
int pack(fb.Builder fbBuilder) {
final int? txIdOffset = txId == null ? null
: fbBuilder.writeString(txId!);
fbBuilder.startTable(3);
fbBuilder.addOffset(0, txIdOffset);
fbBuilder.addUint32(1, vout);
fbBuilder.addUint64(2, value);
return fbBuilder.endTable();
}
@override
String toString() {
return 'BtcinputT{txId: ${txId}, vout: ${vout}, value: ${value}}';
}
}
class _BtcinputReader extends fb.TableReader<Btcinput> {
const _BtcinputReader();
@override
Btcinput createObject(fb.BufferContext bc, int offset) =>
Btcinput._(bc, offset);
}
class BtcinputBuilder {
BtcinputBuilder(this.fbBuilder);
final fb.Builder fbBuilder;
void begin() {
fbBuilder.startTable(3);
}
int addTxIdOffset(int? offset) {
fbBuilder.addOffset(0, offset);
return fbBuilder.offset;
}
int addVout(int? vout) {
fbBuilder.addUint32(1, vout);
return fbBuilder.offset;
}
int addValue(int? value) {
fbBuilder.addUint64(2, value);
return fbBuilder.offset;
}
int finish() {
return fbBuilder.endTable();
}
}
class BtcinputObjectBuilder extends fb.ObjectBuilder {
final String? _txId;
final int? _vout;
final int? _value;
BtcinputObjectBuilder({
String? txId,
int? vout,
int? value,
})
: _txId = txId,
_vout = vout,
_value = value;
/// Finish building, and store into the [fbBuilder].
@override
int finish(fb.Builder fbBuilder) {
final int? txIdOffset = _txId == null ? null
: fbBuilder.writeString(_txId!);
fbBuilder.startTable(3);
fbBuilder.addOffset(0, txIdOffset);
fbBuilder.addUint32(1, _vout);
fbBuilder.addUint64(2, _value);
return fbBuilder.endTable();
}
/// Convenience method to serialize to byte list.
@override
Uint8List toBytes([String? fileIdentifier]) {
final fbBuilder = fb.Builder(deduplicateTables: false);
fbBuilder.finish(finish(fbBuilder), fileIdentifier);
return fbBuilder.buffer;
}
}
class Btcoutput {
Btcoutput._(this._bc, this._bcOffset);
factory Btcoutput(List<int> bytes) {
final rootRef = fb.BufferContext.fromBytes(bytes);
return reader.read(rootRef, 0);
}
static const fb.Reader<Btcoutput> reader = _BtcoutputReader();
final fb.BufferContext _bc;
final int _bcOffset;
String? get scriptPubkey => const fb.StringReader().vTableGetNullable(_bc, _bcOffset, 4);
int get value => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 6, 0);
@override
String toString() {
return 'Btcoutput{scriptPubkey: ${scriptPubkey}, value: ${value}}';
}
BtcoutputT unpack() => BtcoutputT(
scriptPubkey: scriptPubkey,
value: value);
static int pack(fb.Builder fbBuilder, BtcoutputT? object) {
if (object == null) return 0;
return object.pack(fbBuilder);
}
}
class BtcoutputT implements fb.Packable {
String? scriptPubkey;
int value;
BtcoutputT({
this.scriptPubkey,
this.value = 0});
@override
int pack(fb.Builder fbBuilder) {
final int? scriptPubkeyOffset = scriptPubkey == null ? null
: fbBuilder.writeString(scriptPubkey!);
fbBuilder.startTable(2);
fbBuilder.addOffset(0, scriptPubkeyOffset);
fbBuilder.addUint64(1, value);
return fbBuilder.endTable();
}
@override
String toString() {
return 'BtcoutputT{scriptPubkey: ${scriptPubkey}, value: ${value}}';
}
}
class _BtcoutputReader extends fb.TableReader<Btcoutput> {
const _BtcoutputReader();
@override
Btcoutput createObject(fb.BufferContext bc, int offset) =>
Btcoutput._(bc, offset);
}
class BtcoutputBuilder {
BtcoutputBuilder(this.fbBuilder);
final fb.Builder fbBuilder;
void begin() {
fbBuilder.startTable(2);
}
int addScriptPubkeyOffset(int? offset) {
fbBuilder.addOffset(0, offset);
return fbBuilder.offset;
}
int addValue(int? value) {
fbBuilder.addUint64(1, value);
return fbBuilder.offset;
}
int finish() {
return fbBuilder.endTable();
}
}
class BtcoutputObjectBuilder extends fb.ObjectBuilder {
final String? _scriptPubkey;
final int? _value;
BtcoutputObjectBuilder({
String? scriptPubkey,
int? value,
})
: _scriptPubkey = scriptPubkey,
_value = value;
/// Finish building, and store into the [fbBuilder].
@override
int finish(fb.Builder fbBuilder) {
final int? scriptPubkeyOffset = _scriptPubkey == null ? null
: fbBuilder.writeString(_scriptPubkey!);
fbBuilder.startTable(2);
fbBuilder.addOffset(0, scriptPubkeyOffset);
fbBuilder.addUint64(1, _value);
return fbBuilder.endTable();
}
/// Convenience method to serialize to byte list.
@override
Uint8List toBytes([String? fileIdentifier]) {
final fbBuilder = fb.Builder(deduplicateTables: false);
fbBuilder.finish(finish(fbBuilder), fileIdentifier);
return fbBuilder.buffer;
}
}
class Btctx {
Btctx._(this._bc, this._bcOffset);
factory Btctx(List<int> bytes) {
final rootRef = fb.BufferContext.fromBytes(bytes);
return reader.read(rootRef, 0);
}
static const fb.Reader<Btctx> reader = _BtctxReader();
final fb.BufferContext _bc;
final int _bcOffset;
List<Btcinput>? get txins => const fb.ListReader<Btcinput>(Btcinput.reader).vTableGetNullable(_bc, _bcOffset, 4);
List<Btcoutput>? get txouts => const fb.ListReader<Btcoutput>(Btcoutput.reader).vTableGetNullable(_bc, _bcOffset, 6);
int get fee => const fb.Uint64Reader().vTableGet(_bc, _bcOffset, 8, 0);
@override
String toString() {
return 'Btctx{txins: ${txins}, txouts: ${txouts}, fee: ${fee}}';
}
BtctxT unpack() => BtctxT(
txins: txins?.map((e) => e.unpack()).toList(),
txouts: txouts?.map((e) => e.unpack()).toList(),
fee: fee);
static int pack(fb.Builder fbBuilder, BtctxT? object) {
if (object == null) return 0;
return object.pack(fbBuilder);
}
}
class BtctxT implements fb.Packable {
List<BtcinputT>? txins;
List<BtcoutputT>? txouts;
int fee;
BtctxT({
this.txins,
this.txouts,
this.fee = 0});
@override
int pack(fb.Builder fbBuilder) {
final int? txinsOffset = txins == null ? null
: fbBuilder.writeList(txins!.map((b) => b.pack(fbBuilder)).toList());
final int? txoutsOffset = txouts == null ? null
: fbBuilder.writeList(txouts!.map((b) => b.pack(fbBuilder)).toList());
fbBuilder.startTable(3);
fbBuilder.addOffset(0, txinsOffset);
fbBuilder.addOffset(1, txoutsOffset);
fbBuilder.addUint64(2, fee);
return fbBuilder.endTable();
}
@override
String toString() {
return 'BtctxT{txins: ${txins}, txouts: ${txouts}, fee: ${fee}}';
}
}
class _BtctxReader extends fb.TableReader<Btctx> {
const _BtctxReader();
@override
Btctx createObject(fb.BufferContext bc, int offset) =>
Btctx._(bc, offset);
}
class BtctxBuilder {
BtctxBuilder(this.fbBuilder);
final fb.Builder fbBuilder;
void begin() {
fbBuilder.startTable(3);
}
int addTxinsOffset(int? offset) {
fbBuilder.addOffset(0, offset);
return fbBuilder.offset;
}
int addTxoutsOffset(int? offset) {
fbBuilder.addOffset(1, offset);
return fbBuilder.offset;
}
int addFee(int? fee) {
fbBuilder.addUint64(2, fee);
return fbBuilder.offset;
}
int finish() {
return fbBuilder.endTable();
}
}
class BtctxObjectBuilder extends fb.ObjectBuilder {
final List<BtcinputObjectBuilder>? _txins;
final List<BtcoutputObjectBuilder>? _txouts;
final int? _fee;
BtctxObjectBuilder({
List<BtcinputObjectBuilder>? txins,
List<BtcoutputObjectBuilder>? txouts,
int? fee,
})
: _txins = txins,
_txouts = txouts,
_fee = fee;
/// Finish building, and store into the [fbBuilder].
@override
int finish(fb.Builder fbBuilder) {
final int? txinsOffset = _txins == null ? null
: fbBuilder.writeList(_txins!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
final int? txoutsOffset = _txouts == null ? null
: fbBuilder.writeList(_txouts!.map((b) => b.getOrCreateOffset(fbBuilder)).toList());
fbBuilder.startTable(3);
fbBuilder.addOffset(0, txinsOffset);
fbBuilder.addOffset(1, txoutsOffset);
fbBuilder.addUint64(2, _fee);
return fbBuilder.endTable();
}
/// Convenience method to serialize to byte list.
@override
Uint8List toBytes([String? fileIdentifier]) {
final fbBuilder = fb.Builder(deduplicateTables: false);
fbBuilder.finish(finish(fbBuilder), fileIdentifier);
return fbBuilder.buffer;
}
}
class Message {
Message._(this._bc, this._bcOffset);
factory Message(List<int> bytes) {

View File

@ -117,10 +117,6 @@ class WarpApi {
name.toNativeUtf8().cast<Int8>(), index, count);
}
static String ledgerGetFVK(int coin) {
return unwrapResultString(warp_api_lib.ledger_get_fvk(coin));
}
static void convertToWatchOnly(int coin, int id) {
warp_api_lib.convert_to_watchonly(coin, id);
}
@ -159,8 +155,8 @@ class WarpApi {
return unwrapResultU32(warp_api_lib.rewind_to(height));
}
static void rescanFrom(int height) {
warp_api_lib.rescan_from(height);
static void rescanFrom(int coin, int height) {
warp_api_lib.rescan_from(coin, height);
}
static int warpSync(SyncParams params) {
@ -174,8 +170,8 @@ class WarpApi {
warp_api_lib.cancel_warp();
}
static Future<int> getLatestHeight() async {
return await compute(getLatestHeightIsolateFn, null);
static Future<int> getLatestHeight(int coin) async {
return await compute(getLatestHeightIsolateFn, coin);
}
static int validKey(int coin, String key) {
@ -221,8 +217,8 @@ class WarpApi {
// recipientJson, useTransparent, anchorOffset, receivePort.sendPort));
// }
static int getTBalance() {
final balance = warp_api_lib.get_taddr_balance(0xFF, 0);
static int getTBalance(int coin, int account) {
final balance = warp_api_lib.get_taddr_balance(coin, account);
return unwrapResultU64(balance);
}
@ -303,8 +299,9 @@ class WarpApi {
SignOnlyParams(coin, account, tx, receivePort.sendPort));
}
static String broadcast(String txStr) {
final res = warp_api_lib.broadcast_tx(txStr.toNativeUtf8().cast<Int8>());
static String broadcast(int coin, String txStr) {
final res =
warp_api_lib.broadcast_tx(coin, txStr.toNativeUtf8().cast<Int8>());
return unwrapResultString(res);
}
@ -341,9 +338,9 @@ class WarpApi {
return res;
}
static Future<int> syncHistoricalPrices(String currency) async {
return await compute(
syncHistoricalPricesIsolateFn, SyncHistoricalPricesParams(currency));
static Future<int> syncHistoricalPrices(int coin, String currency) async {
return await compute(syncHistoricalPricesIsolateFn,
SyncHistoricalPricesParams(coin, currency));
}
static void setDbPasswd(int coin, String passwd) {
@ -455,14 +452,6 @@ class WarpApi {
return kp;
}
static void ledgerBuildKeys() {
unwrapResultU8(warp_api_lib.ledger_build_keys());
}
static String ledgerGetAddress() {
return unwrapResultString(warp_api_lib.ledger_get_address());
}
static Future<String> ledgerSend(int coin, String txPlan) async {
return await compute((_) {
return unwrapResultString(
@ -667,8 +656,8 @@ String signOnlyIsolateFn(SignOnlyParams params) {
return convertCString(txIdRes.value);
}
int getLatestHeightIsolateFn(Null n) {
return unwrapResultU32(warp_api_lib.get_latest_height());
int getLatestHeightIsolateFn(int coin) {
return unwrapResultU32(warp_api_lib.get_latest_height(coin));
}
String transferPoolsIsolateFn(TransferPoolsParams params) {
@ -689,6 +678,7 @@ int syncHistoricalPricesIsolateFn(SyncHistoricalPricesParams params) {
final now = DateTime.now();
final today = DateTime.utc(now.year, now.month, now.day);
return unwrapResultU32(warp_api_lib.sync_historical_prices(
params.coin,
today.millisecondsSinceEpoch ~/ 1000,
365,
params.currency.toNativeUtf8().cast<Int8>()));
@ -768,9 +758,10 @@ class TransferPoolsParams {
}
class SyncHistoricalPricesParams {
final int coin;
final String currency;
SyncHistoricalPricesParams(this.currency);
SyncHistoricalPricesParams(this.coin, this.currency);
}
class GetTBalanceParams {

View File

@ -415,8 +415,12 @@ class NativeLibrary {
late final _dart_get_diversified_address _get_diversified_address =
_get_diversified_address_ptr.asFunction<_dart_get_diversified_address>();
CResult_u32 get_latest_height() {
return _get_latest_height();
CResult_u32 get_latest_height(
int coin,
) {
return _get_latest_height(
coin,
);
}
late final _get_latest_height_ptr =
@ -424,37 +428,6 @@ class NativeLibrary {
late final _dart_get_latest_height _get_latest_height =
_get_latest_height_ptr.asFunction<_dart_get_latest_height>();
CResult_u8 ledger_build_keys() {
return _ledger_build_keys();
}
late final _ledger_build_keys_ptr =
_lookup<ffi.NativeFunction<_c_ledger_build_keys>>('ledger_build_keys');
late final _dart_ledger_build_keys _ledger_build_keys =
_ledger_build_keys_ptr.asFunction<_dart_ledger_build_keys>();
CResult_____c_char ledger_get_fvk(
int coin,
) {
return _ledger_get_fvk(
coin,
);
}
late final _ledger_get_fvk_ptr =
_lookup<ffi.NativeFunction<_c_ledger_get_fvk>>('ledger_get_fvk');
late final _dart_ledger_get_fvk _ledger_get_fvk =
_ledger_get_fvk_ptr.asFunction<_dart_ledger_get_fvk>();
CResult_____c_char ledger_get_address() {
return _ledger_get_address();
}
late final _ledger_get_address_ptr =
_lookup<ffi.NativeFunction<_c_ledger_get_address>>('ledger_get_address');
late final _dart_ledger_get_address _ledger_get_address =
_ledger_get_address_ptr.asFunction<_dart_ledger_get_address>();
void skip_to_last_height(
int coin,
) {
@ -483,9 +456,11 @@ class NativeLibrary {
_rewind_to_ptr.asFunction<_dart_rewind_to>();
void rescan_from(
int coin,
int height,
) {
return _rescan_from(
coin,
height,
);
}
@ -649,9 +624,11 @@ class NativeLibrary {
_sign_and_broadcast_ptr.asFunction<_dart_sign_and_broadcast>();
CResult_____c_char broadcast_tx(
int coin,
ffi.Pointer<ffi.Int8> tx_str,
) {
return _broadcast_tx(
coin,
tx_str,
);
}
@ -717,11 +694,13 @@ class NativeLibrary {
_get_block_by_time_ptr.asFunction<_dart_get_block_by_time>();
CResult_u32 sync_historical_prices(
int coin,
int now,
int days,
ffi.Pointer<ffi.Int8> currency,
) {
return _sync_historical_prices(
coin,
now,
days,
currency,
@ -1655,6 +1634,16 @@ const int ShieldedTx_VT_MEMO = 20;
const int ShieldedTxVec_VT_TXS = 4;
const int PlainNote_VT_VOUT = 12;
const int BTCOutput_VT_SCRIPT_PUBKEY = 4;
const int BTCTx_VT_TXINS = 4;
const int BTCTx_VT_TXOUTS = 6;
const int BTCTx_VT_FEE = 8;
const int Message_VT_ID_MSG = 4;
const int Message_VT_ID_TX = 6;
@ -1743,8 +1732,6 @@ const int TxReport_VT_NET_SAPLING = 12;
const int TxReport_VT_NET_ORCHARD = 14;
const int TxReport_VT_FEE = 16;
const int TxReport_VT_PRIVACY_LEVEL = 18;
typedef _c_dummy_export = ffi.Void Function();
@ -2005,26 +1992,14 @@ typedef _dart_get_diversified_address = CResult_____c_char Function(
int time,
);
typedef _c_get_latest_height = CResult_u32 Function();
typedef _dart_get_latest_height = CResult_u32 Function();
typedef _c_ledger_build_keys = CResult_u8 Function();
typedef _dart_ledger_build_keys = CResult_u8 Function();
typedef _c_ledger_get_fvk = CResult_____c_char Function(
typedef _c_get_latest_height = CResult_u32 Function(
ffi.Uint8 coin,
);
typedef _dart_ledger_get_fvk = CResult_____c_char Function(
typedef _dart_get_latest_height = CResult_u32 Function(
int coin,
);
typedef _c_ledger_get_address = CResult_____c_char Function();
typedef _dart_ledger_get_address = CResult_____c_char Function();
typedef _c_skip_to_last_height = ffi.Void Function(
ffi.Uint8 coin,
);
@ -2042,10 +2017,12 @@ typedef _dart_rewind_to = CResult_u32 Function(
);
typedef _c_rescan_from = ffi.Void Function(
ffi.Uint8 coin,
ffi.Uint32 height,
);
typedef _dart_rescan_from = void Function(
int coin,
int height,
);
@ -2162,10 +2139,12 @@ typedef _dart_sign_and_broadcast = CResult_____c_char Function(
);
typedef _c_broadcast_tx = CResult_____c_char Function(
ffi.Uint8 coin,
ffi.Pointer<ffi.Int8> tx_str,
);
typedef _dart_broadcast_tx = CResult_____c_char Function(
int coin,
ffi.Pointer<ffi.Int8> tx_str,
);
@ -2204,12 +2183,14 @@ typedef _dart_get_block_by_time = CResult_u32 Function(
);
typedef _c_sync_historical_prices = CResult_u32 Function(
ffi.Uint8 coin,
ffi.Int64 now,
ffi.Uint32 days,
ffi.Pointer<ffi.Int8> currency,
);
typedef _dart_sync_historical_prices = CResult_u32 Function(
int coin,
int now,
int days,
ffi.Pointer<ffi.Int8> currency,

View File

@ -30,7 +30,7 @@ ffigen:
- '../../native/zcash-sync/binding.h'
# On MacOS
llvm-path:
- '/opt/homebrew/Cellar/llvm/15.0.7_1'
- '/opt/homebrew/Cellar/llvm/16.0.5'
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

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.4.1+447
version: 1.4.1+454
environment:
sdk: ">=2.12.0 <3.0.0"
@ -147,6 +147,7 @@ flutter:
- assets/multipay.svg
- assets/ycash.png
- assets/zcash.png
- assets/bitcoin.png
- assets/success.mp3
- assets/fail.mp3
- assets/ding.mp3