2021-07-07 08:40:05 -07:00
import ' dart:isolate ' ;
2021-07-10 22:20:53 -07:00
import ' dart:typed_data ' ;
2021-07-12 04:32:49 -07:00
import ' dart:math ' as math ;
2021-07-07 08:40:05 -07:00
2021-07-12 04:32:49 -07:00
import ' package:charts_flutter/flutter.dart ' as charts show MaterialPalette ;
2021-06-26 07:30:12 -07:00
import ' package:flutter/material.dart ' ;
import ' package:intl/intl.dart ' ;
import ' package:mobx/mobx.dart ' ;
import ' package:path/path.dart ' ;
import ' package:sqflite/sqflite.dart ' ;
import ' package:warp_api/warp_api.dart ' ;
import ' package:shared_preferences/shared_preferences.dart ' ;
import ' package:http/http.dart ' as http ;
import ' dart:convert ' as convert ;
import ' package:convert/convert.dart ' ;
2021-07-10 22:20:53 -07:00
import ' package:flex_color_scheme/flex_color_scheme.dart ' ;
2021-06-26 07:30:12 -07:00
import ' main.dart ' ;
part ' store.g.dart ' ;
class Settings = _Settings with _ $Settings ;
abstract class _Settings with Store {
2021-07-09 06:33:39 -07:00
@ observable
String ldUrl ;
@ observable
String ldUrlChoice ;
@ observable
int anchorOffset ;
2021-07-09 22:44:34 -07:00
@ observable
bool getTx ;
2021-07-10 22:20:53 -07:00
@ observable
int rowsPerPage ;
@ observable
String theme ;
@ observable
String themeBrightness ;
@ observable
2021-07-12 04:32:49 -07:00
ThemeData themeData = ThemeData . light ( ) ;
var palette = charts . MaterialPalette . blue ;
2021-06-26 07:30:12 -07:00
@ action
2021-07-12 04:32:49 -07:00
Future < bool > restore ( ) async {
2021-06-26 07:30:12 -07:00
final prefs = await SharedPreferences . getInstance ( ) ;
2021-07-07 08:40:05 -07:00
final prefMode = prefs . getString ( ' theme ' ) ? ? " light " ;
2021-07-09 06:33:39 -07:00
ldUrlChoice = prefs . getString ( ' lightwalletd_choice ' ) ? ? " lightwalletd " ;
ldUrl = prefs . getString ( ' lightwalletd_custom ' ) ? ? " " ;
prefs . setString ( ' lightwalletd_choice ' , ldUrlChoice ) ;
prefs . setString ( ' lightwalletd_custom ' , ldUrl ) ;
anchorOffset = prefs . getInt ( ' anchor_offset ' ) ? ? 3 ;
2021-07-09 22:44:34 -07:00
getTx = prefs . getBool ( ' get_txinfo ' ) ? ? true ;
2021-07-10 22:20:53 -07:00
rowsPerPage = prefs . getInt ( ' rows_per_age ' ) ? ? 10 ;
theme = prefs . getString ( ' theme ' ) ? ? " zcash " ;
themeBrightness = prefs . getString ( ' theme_brightness ' ) ? ? " dark " ;
_updateThemeData ( ) ;
2021-07-12 04:32:49 -07:00
return true ;
2021-06-26 07:30:12 -07:00
}
2021-07-09 06:33:39 -07:00
@ action
Future < void > setURLChoice ( String choice ) async {
ldUrlChoice = choice ;
final prefs = await SharedPreferences . getInstance ( ) ;
prefs . setString ( ' lightwalletd_choice ' , ldUrlChoice ) ;
updateLWD ( ) ;
}
@ action
Future < void > setURL ( String url ) async {
ldUrl = url ;
final prefs = await SharedPreferences . getInstance ( ) ;
prefs . setString ( ' lightwalletd_custom ' , ldUrl ) ;
updateLWD ( ) ;
}
@ action
Future < void > setAnchorOffset ( int offset ) async {
final prefs = await SharedPreferences . getInstance ( ) ;
anchorOffset = offset ;
prefs . setInt ( ' anchor_offset ' , offset ) ;
}
2021-07-10 22:20:53 -07:00
@ action
Future < void > setTheme ( String thm ) async {
final prefs = await SharedPreferences . getInstance ( ) ;
theme = thm ;
prefs . setString ( ' theme ' , thm ) ;
_updateThemeData ( ) ;
}
@ action
Future < void > setThemeBrightness ( String brightness ) async {
print ( brightness ) ;
final prefs = await SharedPreferences . getInstance ( ) ;
themeBrightness = brightness ;
prefs . setString ( ' theme_brightness ' , brightness ) ;
_updateThemeData ( ) ;
}
void _updateThemeData ( ) {
FlexScheme scheme ;
switch ( theme ) {
2021-07-12 04:32:49 -07:00
case ' zcash ' :
scheme = FlexScheme . mango ;
palette = charts . MaterialPalette . gray ;
break ;
case ' blue ' :
scheme = FlexScheme . bahamaBlue ;
palette = charts . MaterialPalette . blue ;
break ;
case ' pink ' :
scheme = FlexScheme . sakura ;
palette = charts . MaterialPalette . pink ;
break ;
case ' coffee ' :
scheme = FlexScheme . espresso ;
palette = charts . MaterialPalette . gray ;
break ;
2021-07-10 22:20:53 -07:00
}
switch ( themeBrightness ) {
case ' light ' : themeData = FlexColorScheme . light ( scheme: scheme ) . toTheme ; break ;
case ' dark ' : themeData = FlexColorScheme . dark ( scheme: scheme ) . toTheme ; break ;
}
}
2021-07-09 06:33:39 -07:00
String getLWD ( ) {
switch ( ldUrlChoice ) {
case " custom " : return ldUrl ;
case " lightwalletd " : return " https://mainnet.lightwalletd.com:9067 " ;
case " zecwallet " : return " https://lwdv3.zecwallet.co " ;
}
}
void updateLWD ( ) {
WarpApi . updateLWD ( getLWD ( ) ) ;
}
2021-07-09 22:44:34 -07:00
@ action
Future < void > updateGetTx ( bool v ) async {
final prefs = await SharedPreferences . getInstance ( ) ;
getTx = v ;
prefs . setBool ( ' get_txinfo ' , v ) ;
}
2021-07-10 22:20:53 -07:00
@ action
Future < void > setRowsPerPage ( int v ) async {
final prefs = await SharedPreferences . getInstance ( ) ;
rowsPerPage = v ;
prefs . setInt ( ' rows_per_age ' , v ) ;
}
2021-06-26 07:30:12 -07:00
}
class AccountManager = _AccountManager with _ $AccountManager ;
abstract class _AccountManager with Store {
Database db ;
@ observable
Account active ;
@ observable
bool canPay = false ;
@ observable
int balance = 0 ;
@ observable
int unconfirmedBalance = 0 ;
2021-07-09 06:33:39 -07:00
@ observable
String taddress = " " ;
@ observable
int tbalance = 0 ;
2021-06-26 07:30:12 -07:00
@ observable
List < Note > notes = [ ] ;
@ observable
List < Tx > txs = [ ] ;
2021-07-12 04:32:49 -07:00
@ observable
2021-07-18 08:59:02 -07:00
int dataEpoch = 0 ;
2021-07-12 04:32:49 -07:00
@ observable
List < Spending > spendings = [ ] ;
@ observable
List < AccountBalance > accountBalances = [ ] ;
2021-06-26 07:30:12 -07:00
@ observable
2021-07-07 08:40:05 -07:00
List < Account > accounts = [ ] ;
2021-06-26 07:30:12 -07:00
2021-07-12 04:32:49 -07:00
@ observable
SortOrder noteSortOrder = SortOrder . Unsorted ;
@ observable
SortOrder txSortOrder = SortOrder . Unsorted ;
2021-07-18 08:59:02 -07:00
@ observable
List < Contact > contacts = [ ] ;
2021-06-26 07:30:12 -07:00
Future < void > init ( ) async {
db = await getDatabase ( ) ;
await resetToDefaultAccount ( ) ;
}
Future < void > resetToDefaultAccount ( ) async {
await refresh ( ) ;
if ( accounts . isNotEmpty ) {
final prefs = await SharedPreferences . getInstance ( ) ;
final account = prefs . getInt ( ' account ' ) ? ? accounts [ 0 ] . id ;
setActiveAccountId ( account ) ;
}
}
refresh ( ) async {
accounts = await _list ( ) ;
}
@ action
Future < void > setActiveAccount ( Account account ) async {
2021-07-07 08:40:05 -07:00
if ( account = = null ) return ;
2021-06-26 07:30:12 -07:00
final prefs = await SharedPreferences . getInstance ( ) ;
prefs . setInt ( ' account ' , account . id ) ;
2021-07-09 06:33:39 -07:00
final List < Map > res1 = await db . rawQuery (
" SELECT address FROM taddrs WHERE account = ?1 " , [ account . id ] ) ;
if ( res1 . isNotEmpty ) taddress = res1 [ 0 ] [ ' address ' ] ;
2021-06-26 07:30:12 -07:00
WarpApi . setMempoolAccount ( account . id ) ;
2021-07-09 06:33:39 -07:00
final List < Map > res2 = await db . rawQuery (
" SELECT sk FROM accounts WHERE id_account = ?1 " , [ account . id ] ) ;
canPay = res2 . isNotEmpty & & res2 [ 0 ] [ ' sk ' ] ! = null ;
2021-07-12 04:32:49 -07:00
await _fetchData ( account . id ) ;
2021-07-09 06:33:39 -07:00
active = account ;
print ( " Active account = ${ account . id } " ) ;
2021-06-26 07:30:12 -07:00
}
@ action
2021-07-09 06:33:39 -07:00
Future < void > setActiveAccountId ( int idAccount ) async {
2021-06-26 07:30:12 -07:00
final account = accounts . firstWhere ( ( account ) = > account . id = = idAccount ,
2021-07-07 08:40:05 -07:00
orElse: ( ) = > accounts . isNotEmpty ? accounts [ 0 ] : null ) ;
2021-07-09 06:33:39 -07:00
await setActiveAccount ( account ) ;
2021-06-26 07:30:12 -07:00
}
2021-07-07 21:22:54 -07:00
String newAddress ( ) {
return WarpApi . newAddress ( active . id ) ;
}
2021-07-12 04:32:49 -07:00
Future < Backup > getBackup ( ) async {
2021-07-07 08:40:05 -07:00
final List < Map > res = await db . rawQuery (
" SELECT seed, sk, ivk FROM accounts WHERE id_account = ?1 " ,
[ active . id ] ) ;
2021-06-26 07:30:12 -07:00
if ( res . isEmpty ) return null ;
final row = res [ 0 ] ;
2021-07-12 04:32:49 -07:00
final seed = row [ ' seed ' ] ;
final sk = row [ ' sk ' ] ;
final ivk = row [ ' ivk ' ] ;
int type ;
if ( seed ! = null ) type = 0 ;
else if ( sk ! = null ) type = 1 ;
else if ( ivk ! = null ) type = 2 ;
return Backup ( type , seed , sk , ivk ) ;
2021-06-26 07:30:12 -07:00
}
2021-07-09 06:33:39 -07:00
Future < int > _getBalance ( int accountId ) async {
2021-07-07 08:40:05 -07:00
final List < Map > res = await db . rawQuery (
" SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND (spent IS NULL OR spent = 0) " ,
2021-07-09 06:33:39 -07:00
[ accountId ] ) ;
2021-06-26 07:30:12 -07:00
if ( res . isEmpty ) return 0 ;
return res [ 0 ] [ ' value ' ] ? ? 0 ;
}
Future < int > getBalanceSpendable ( int height ) async {
2021-07-07 08:40:05 -07:00
final List < Map > res = await db . rawQuery (
2021-07-12 04:32:49 -07:00
" SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND (spent IS NULL OR spent = 0) "
" AND height <= ?2 AND (excluded IS NULL OR NOT excluded) " ,
2021-07-07 08:40:05 -07:00
[ active . id , height ] ) ;
2021-06-26 07:30:12 -07:00
if ( res . isEmpty ) return 0 ;
return res [ 0 ] [ ' value ' ] ? ? 0 ;
}
@ action
Future < void > updateUnconfirmedBalance ( ) async {
unconfirmedBalance = await WarpApi . mempoolSync ( ) ;
}
isEmpty ( ) async {
2021-07-07 08:40:05 -07:00
final List < Map > res = await db . rawQuery ( " SELECT name FROM accounts " , [ ] ) ;
2021-06-26 07:30:12 -07:00
return res . isEmpty ;
}
Future < List < Account > > _list ( ) async {
final List < Map > res = await db . rawQuery (
2021-07-07 08:40:05 -07:00
" WITH notes AS (SELECT a.id_account, a.name, a.address, 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) "
" SELECT id_account, name, address, COALESCE(sum(nv), 0) AS balance FROM notes GROUP by id_account " ,
[ ] ) ;
return res
. map ( ( r ) = >
Account ( r [ ' id_account ' ] , r [ ' name ' ] , r [ ' address ' ] , r [ ' balance ' ] ) )
. toList ( ) ;
2021-06-26 07:30:12 -07:00
}
@ action
Future < void > delete ( int account ) async {
await db . rawDelete ( " DELETE FROM accounts WHERE id_account = ?1 " , [ account ] ) ;
2021-07-09 06:33:39 -07:00
await db . rawDelete ( " DELETE FROM taddrs WHERE account = ?1 " , [ account ] ) ;
2021-06-26 07:30:12 -07:00
}
2021-07-07 08:40:05 -07:00
@ action
Future < void > updateBalance ( ) async {
if ( active = = null ) return ;
2021-07-09 06:33:39 -07:00
balance = await _getBalance ( active . id ) ;
2021-07-07 08:40:05 -07:00
}
@ action
2021-07-12 04:32:49 -07:00
Future < void > fetchAccountData ( ) async {
2021-07-07 08:40:05 -07:00
if ( active = = null ) return ;
2021-07-12 04:32:49 -07:00
await _fetchData ( active . id ) ;
}
Future < void > _fetchData ( int accountId ) async {
await _fetchNotesAndHistory ( accountId ) ;
await _fetchSpending ( accountId ) ;
await _fetchAccountBalanceTimeSeries ( accountId ) ;
2021-07-18 08:59:02 -07:00
await _fetchContacts ( accountId ) ;
dataEpoch = DateTime . now ( ) . millisecondsSinceEpoch ;
2021-07-09 06:33:39 -07:00
}
2021-07-10 22:20:53 -07:00
final DateFormat noteDateFormat = DateFormat ( " yy-MM-dd HH:mm " ) ;
final DateFormat txDateFormat = DateFormat ( " MM-dd HH:mm " ) ;
2021-07-09 06:33:39 -07:00
Future < void > _updateBalance ( int accountId ) async {
balance = await _getBalance ( accountId ) ;
}
Future < void > _fetchNotesAndHistory ( int accountId ) async {
await _updateBalance ( accountId ) ;
2021-06-26 07:30:12 -07:00
final List < Map > res = await db . rawQuery (
2021-07-12 04:32:49 -07:00
" SELECT n.id_note, n.height, n.value, t.timestamp, n.excluded 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 " ,
2021-07-09 06:33:39 -07:00
[ accountId ] ) ;
2021-06-26 07:30:12 -07:00
notes = res . map ( ( row ) {
2021-07-12 04:32:49 -07:00
final id = row [ ' id_note ' ] ;
2021-06-26 07:30:12 -07:00
final height = row [ ' height ' ] ;
2021-07-10 22:20:53 -07:00
final timestamp = noteDateFormat
2021-07-07 08:40:05 -07:00
. format ( DateTime . fromMillisecondsSinceEpoch ( row [ ' timestamp ' ] * 1000 ) ) ;
2021-07-12 04:32:49 -07:00
final excluded = ( row [ ' excluded ' ] ? ? 0 ) ! = 0 ;
return Note ( id , height , timestamp , row [ ' value ' ] / ZECUNIT , excluded ) ;
2021-06-26 07:30:12 -07:00
} ) . toList ( ) ;
2021-07-12 04:32:49 -07:00
_sortNoteAmount ( noteSortOrder ) ;
2021-06-26 07:30:12 -07:00
final List < Map > res2 = await db . rawQuery (
2021-07-09 22:44:34 -07:00
" SELECT id_tx, txid, height, timestamp, address, value, memo FROM transactions WHERE account = ?1 " ,
2021-07-09 06:33:39 -07:00
[ accountId ] ) ;
2021-06-26 07:30:12 -07:00
txs = res2 . map ( ( row ) {
2021-07-10 22:20:53 -07:00
Uint8List txid = row [ ' txid ' ] ;
final fullTxId = hex . encode ( txid . reversed . toList ( ) ) ;
final shortTxid = fullTxId . substring ( 0 , 8 ) ;
final timestamp = txDateFormat
2021-07-07 08:40:05 -07:00
. format ( DateTime . fromMillisecondsSinceEpoch ( row [ ' timestamp ' ] * 1000 ) ) ;
2021-07-10 22:20:53 -07:00
return Tx ( row [ ' id_tx ' ] , row [ ' height ' ] , timestamp , shortTxid , fullTxId , row [ ' value ' ] / ZECUNIT , row [ ' address ' ] , row [ ' memo ' ] ) ;
2021-06-26 07:30:12 -07:00
} ) . toList ( ) ;
2021-07-12 04:32:49 -07:00
_sortTxAmount ( txSortOrder ) ;
}
@ action
Future < void > sortNoteAmount ( ) async {
noteSortOrder = nextSortOrder ( noteSortOrder ) ;
_sortNoteAmount ( noteSortOrder ) ;
}
void _sortNoteAmount ( SortOrder order ) {
switch ( order ) {
case SortOrder . Ascending:
notes . sort ( ( a , b ) = > a . value . compareTo ( b . value ) ) ;
break ;
case SortOrder . Descending:
notes . sort ( ( a , b ) = > - a . value . compareTo ( b . value ) ) ;
break ;
case SortOrder . Unsorted:
notes . sort ( ( a , b ) = > a . id . compareTo ( b . id ) ) ;
break ;
}
}
@ action
Future < void > sortTxAmount ( ) async {
txSortOrder = nextSortOrder ( txSortOrder ) ;
_sortTxAmount ( txSortOrder ) ;
}
void _sortTxAmount ( SortOrder order ) {
switch ( order ) {
case SortOrder . Ascending:
txs . sort ( ( a , b ) = > a . value . compareTo ( b . value ) ) ;
break ;
case SortOrder . Descending:
txs . sort ( ( a , b ) = > - a . value . compareTo ( b . value ) ) ;
break ;
case SortOrder . Unsorted:
txs . sort ( ( a , b ) = > a . id . compareTo ( b . id ) ) ;
break ;
}
}
Future < void > _fetchSpending ( int accountId ) async {
final cutoff = DateTime . now ( ) . add ( Duration ( days: - 30 ) ) . millisecondsSinceEpoch / 1000 ;
final List < Map > res = await db . rawQuery (
" SELECT SUM(value) as v, address FROM transactions WHERE account = ?1 AND timestamp >= ?2 AND value < 0 GROUP BY address ORDER BY v ASC LIMIT 10 " ,
[ accountId , cutoff ] ) ;
spendings = res . map ( ( row ) {
final address = row [ ' address ' ] ? ? " " ;
final value = - row [ ' v ' ] / ZECUNIT ;
return Spending ( addressLeftTrim ( address ) , value ) ;
} ) . toList ( ) ;
}
Future < void > _fetchAccountBalanceTimeSeries ( int accountId ) async {
final cutoff = DateTime . now ( ) . add ( Duration ( days: - 30 ) ) . millisecondsSinceEpoch / 1000 ;
final List < Map > res = await db . rawQuery (
" SELECT timestamp, value FROM transactions WHERE account = ?1 AND timestamp >= ?2 ORDER BY timestamp DESC " ,
[ accountId , cutoff ] ) ;
accountBalances = [ ] ;
var b = balance ;
accountBalances . add ( AccountBalance ( DateTime . now ( ) , b / ZECUNIT ) ) ;
for ( var row in res ) {
final timestamp = DateTime . fromMillisecondsSinceEpoch ( row [ ' timestamp ' ] * 1000 ) ;
final value = row [ ' value ' ] ;
final ab = AccountBalance ( timestamp , b / ZECUNIT ) ;
accountBalances . add ( ab ) ;
b - = value ;
}
2021-06-26 07:30:12 -07:00
}
2021-07-07 08:40:05 -07:00
@ action
Future < void > convertToWatchOnly ( ) async {
2021-07-09 06:33:39 -07:00
await db . rawUpdate (
" UPDATE accounts SET seed = NULL, sk = NULL WHERE id_account = ?1 " ,
[ active . id ] ) ;
2021-07-07 08:40:05 -07:00
canPay = false ;
}
2021-07-09 06:33:39 -07:00
2021-07-12 04:32:49 -07:00
@ action
Future < void > excludeNote ( Note note ) async {
await db . execute (
" UPDATE received_notes SET excluded = ?2 WHERE id_note = ?1 " ,
[ note . id , note . excluded ] ) ;
}
2021-07-09 06:33:39 -07:00
void updateTBalance ( ) {
if ( active = = null ) return ;
int balance = WarpApi . getTBalance ( active . id ) ;
if ( balance ! = tbalance ) tbalance = balance ;
}
2021-07-18 08:59:02 -07:00
Future < void > _fetchContacts ( int accountId ) async {
List < Map > res = await db . rawQuery ( " SELECT name, address FROM contacts WHERE account = ?1 ORDER BY name " , [ accountId ] ) ;
contacts = [ ] ;
for ( var c in res ) {
final contact = Contact ( c [ ' name ' ] , c [ ' address ' ] ) ;
contacts . add ( contact ) ;
}
}
2021-06-26 07:30:12 -07:00
}
class Account {
final int id ;
final String name ;
final String address ;
final int balance ;
Account ( this . id , this . name , this . address , this . balance ) ;
}
class PriceStore = _PriceStore with _ $PriceStore ;
abstract class _PriceStore with Store {
@ observable
double zecPrice = 0.0 ;
2021-07-07 08:40:05 -07:00
2021-06-26 07:30:12 -07:00
@ action
Future < void > fetchZecPrice ( ) async {
final base = " api.binance.com " ;
2021-07-07 08:40:05 -07:00
final uri = Uri . https ( base , ' /api/v3/avgPrice ' , { ' symbol ' : ' ZECUSDT ' } ) ;
2021-06-26 07:30:12 -07:00
final rep = await http . get ( uri ) ;
if ( rep . statusCode = = 200 ) {
final json = convert . jsonDecode ( rep . body ) as Map < String , dynamic > ;
final price = double . parse ( json [ ' price ' ] ) ;
zecPrice = price ;
}
}
}
class SyncStatus = _SyncStatus with _ $SyncStatus ;
abstract class _SyncStatus with Store {
Database _db ;
init ( ) async {
var databasesPath = await getDatabasesPath ( ) ;
final path = join ( databasesPath , ' zec.db ' ) ;
_db = await openDatabase ( path ) ;
await update ( ) ;
}
@ observable
int syncedHeight = - 1 ;
@ observable
int latestHeight = 0 ;
bool isSynced ( ) {
return syncedHeight < 0 | | syncedHeight = = latestHeight ;
}
@ action
setSyncHeight ( int height ) {
2021-07-07 08:40:05 -07:00
syncedHeight = height ;
2021-06-26 07:30:12 -07:00
}
@ action
Future < bool > update ( ) async {
final _syncedHeight = Sqflite . firstIntValue (
2021-07-07 08:40:05 -07:00
await _db . rawQuery ( " SELECT MAX(height) FROM blocks " ) ) ? ?
2021-06-26 07:30:12 -07:00
0 ;
if ( _syncedHeight > 0 ) syncedHeight = _syncedHeight ;
latestHeight = await WarpApi . getLatestHeight ( ) ;
return syncedHeight = = latestHeight ;
}
}
2021-07-07 08:40:05 -07:00
var progressPort = ReceivePort ( ) ;
var progressStream = progressPort . asBroadcastStream ( ) ;
2021-06-26 07:30:12 -07:00
class Note {
2021-07-12 04:32:49 -07:00
int id ;
2021-06-26 07:30:12 -07:00
int height ;
String timestamp ;
double value ;
2021-07-12 04:32:49 -07:00
bool excluded ;
2021-06-26 07:30:12 -07:00
2021-07-12 04:32:49 -07:00
Note ( this . id , this . height , this . timestamp , this . value , this . excluded ) ;
2021-06-26 07:30:12 -07:00
}
class Tx {
2021-07-09 22:44:34 -07:00
int id ;
2021-06-26 07:30:12 -07:00
int height ;
String timestamp ;
String txid ;
2021-07-09 22:44:34 -07:00
String fullTxId ;
2021-06-26 07:30:12 -07:00
double value ;
2021-07-09 22:44:34 -07:00
String address ;
String memo ;
2021-06-26 07:30:12 -07:00
2021-07-09 22:44:34 -07:00
Tx ( this . id , this . height , this . timestamp , this . txid , this . fullTxId , this . value , this . address , this . memo ) ;
2021-06-26 07:30:12 -07:00
}
2021-07-12 04:32:49 -07:00
class Spending {
final String address ;
final double amount ;
Spending ( this . address , this . amount ) ;
}
class AccountBalance {
final DateTime time ;
final double balance ;
AccountBalance ( this . time , this . balance ) ;
}
class Backup {
int type ;
final String seed ;
final String sk ;
final String ivk ;
Backup ( this . type , this . seed , this . sk , this . ivk ) ;
String value ( ) {
switch ( type ) {
case 0 : return seed ;
case 1 : return sk ;
case 2 : return ivk ;
}
return " " ;
}
}
2021-07-18 08:59:02 -07:00
class Contact {
final String name ;
final String address ;
Contact ( this . name , this . address ) ;
}
2021-07-12 04:32:49 -07:00
String addressLeftTrim ( String address ) = > " ... " + address . substring ( math . max ( address . length - 6 , 0 ) ) ;
enum SortOrder {
Unsorted ,
Ascending ,
Descending ,
}
SortOrder nextSortOrder ( SortOrder order ) = > SortOrder . values [ ( order . index + 1 ) % 3 ] ;
2021-07-18 08:59:02 -07:00