2020-07-02 14:43:35 +01:00
/* global alert */
2019-05-02 16:33:03 -04:00
import AsyncStorage from '@react-native-community/async-storage' ;
2019-08-19 21:18:06 +01:00
import RNSecureKeyStore , { ACCESSIBLE } from 'react-native-secure-key-store' ;
2018-07-22 15:49:59 +01:00
import {
HDLegacyBreadwalletWallet ,
HDSegwitP2SHWallet ,
HDLegacyP2PKHWallet ,
WatchOnlyWallet ,
LegacyWallet ,
SegwitP2SHWallet ,
SegwitBech32Wallet ,
2019-06-01 21:44:39 +01:00
HDSegwitBech32Wallet ,
2019-12-26 20:21:07 -06:00
PlaceholderWallet ,
LightningCustodianWallet ,
2020-04-22 16:13:18 +01:00
HDLegacyElectrumSeedP2PKHWallet ,
2020-05-04 18:52:01 +09:00
HDSegwitElectrumSeedP2WPKHWallet ,
2020-10-05 22:25:14 +01:00
MultisigHDWallet ,
2018-07-22 15:49:59 +01:00
} from './' ;
2020-05-24 06:27:08 -04:00
import DeviceQuickActions from './quick-actions' ;
2020-08-28 12:41:56 +01:00
import { AbstractHDElectrumWallet } from './wallets/abstract-hd-electrum-wallet' ;
2020-09-03 22:26:44 -04:00
import { Platform } from 'react-native' ;
2020-09-07 07:35:08 -04:00
import WatchConnectivity from '../WatchConnectivity' ;
2020-07-01 12:56:52 +01:00
const encryption = require ( '../blue_modules/encryption' ) ;
2020-08-28 12:41:56 +01:00
const Realm = require ( 'realm' ) ;
const createHash = require ( 'create-hash' ) ;
let usedBucketNum = false ;
2018-03-20 22:41:07 +02:00
export class AppStorage {
2018-03-31 01:03:58 +01:00
static FLAG _ENCRYPTED = 'data_encrypted' ;
2018-05-28 20:18:11 +01:00
static LANG = 'lang' ;
2018-12-31 21:27:03 +00:00
static EXCHANGE _RATES = 'currency' ;
2018-12-11 22:52:46 +00:00
static LNDHUB = 'lndhub' ;
2019-07-18 11:22:01 +01:00
static ELECTRUM _HOST = 'electrum_host' ;
static ELECTRUM _TCP _PORT = 'electrum_tcp_port' ;
2020-03-26 16:37:11 +00:00
static ELECTRUM _SSL _PORT = 'electrum_ssl_port' ;
2018-12-31 21:27:03 +00:00
static PREFERRED _CURRENCY = 'preferredCurrency' ;
2019-05-19 20:49:42 +01:00
static ADVANCED _MODE _ENABLED = 'advancedmodeenabled' ;
2020-03-20 15:25:23 +00:00
static DELETE _WALLET _AFTER _UNINSTALL = 'deleteWalletAfterUninstall' ;
2020-06-15 19:47:54 +01:00
static HODL _HODL _API _KEY = 'HODL_HODL_API_KEY' ;
static HODL _HODL _SIGNATURE _KEY = 'HODL_HODL_SIGNATURE_KEY' ;
static HODL _HODL _CONTRACTS = 'HODL_HODL_CONTRACTS' ;
2020-03-20 15:25:23 +00:00
2018-03-20 22:41:07 +02:00
constructor ( ) {
/** {Array.<AbstractWallet>} */
this . wallets = [ ] ;
this . tx _metadata = { } ;
2018-03-31 01:03:58 +01:00
this . cachedPassword = false ;
2018-03-20 22:41:07 +02:00
}
2019-08-19 21:18:06 +01:00
/ * *
* Wrapper for storage call . Secure store works only in RN environment . AsyncStorage is
* used for cli / tests
*
* @ param key
* @ param value
* @ returns { Promise < any > | Promise < any > | Promise < void > | * | Promise | void }
* /
setItem ( key , value ) {
if ( typeof navigator !== 'undefined' && navigator . product === 'ReactNative' ) {
return RNSecureKeyStore . set ( key , value , { accessible : ACCESSIBLE . WHEN _UNLOCKED } ) ;
} else {
return AsyncStorage . setItem ( key , value ) ;
}
}
/ * *
* Wrapper for storage call . Secure store works only in RN environment . AsyncStorage is
* used for cli / tests
*
* @ param key
* @ returns { Promise < any > | * }
* /
getItem ( key ) {
if ( typeof navigator !== 'undefined' && navigator . product === 'ReactNative' ) {
return RNSecureKeyStore . get ( key ) ;
} else {
return AsyncStorage . getItem ( key ) ;
}
}
2019-09-14 00:34:24 -04:00
async setResetOnAppUninstallTo ( value ) {
2020-09-03 22:26:44 -04:00
if ( Platform . OS === 'ios' ) {
await this . setItem ( AppStorage . DELETE _WALLET _AFTER _UNINSTALL , value ? '1' : '' ) ;
try {
RNSecureKeyStore . setResetOnAppUninstallTo ( value ) ;
} catch ( Error ) {
console . warn ( Error ) ;
}
2020-03-20 15:25:23 +00:00
}
2019-09-14 00:34:24 -04:00
}
2018-03-30 19:31:10 +01:00
async storageIsEncrypted ( ) {
let data ;
try {
2019-08-19 21:18:06 +01:00
data = await this . getItem ( AppStorage . FLAG _ENCRYPTED ) ;
2018-03-30 19:31:10 +01:00
} catch ( error ) {
return false ;
}
2018-03-31 01:03:58 +01:00
return ! ! data ;
}
2019-08-23 03:04:23 -04:00
async isPasswordInUse ( password ) {
try {
let data = await this . getItem ( 'data' ) ;
data = this . decryptData ( data , password ) ;
2020-03-20 15:25:23 +00:00
return ! ! data ;
2019-08-23 03:04:23 -04:00
} catch ( _e ) {
return false ;
}
}
2018-03-31 01:03:58 +01:00
/ * *
* Iterates through all values of ` data ` trying to
* decrypt each one , and returns first one successfully decrypted
*
2020-03-20 15:25:23 +00:00
* @ param data { string } Serialized array
2018-03-31 01:03:58 +01:00
* @ param password
2020-03-20 15:25:23 +00:00
* @ returns { boolean | string } Either STRING of storage data ( which is stringified JSON ) or FALSE , which means failure
2018-03-31 01:03:58 +01:00
* /
decryptData ( data , password ) {
data = JSON . parse ( data ) ;
let decrypted ;
2020-08-28 12:41:56 +01:00
let num = 0 ;
2020-06-01 15:54:23 +03:00
for ( const value of data ) {
2018-03-31 01:03:58 +01:00
try {
decrypted = encryption . decrypt ( value , password ) ;
} catch ( e ) {
console . log ( e . message ) ;
}
if ( decrypted ) {
2020-08-28 12:41:56 +01:00
usedBucketNum = num ;
2018-03-31 01:03:58 +01:00
return decrypted ;
}
2020-08-28 12:41:56 +01:00
num ++ ;
2018-03-30 19:31:10 +01:00
}
2018-03-31 01:03:58 +01:00
return false ;
}
2019-08-23 03:04:23 -04:00
async decryptStorage ( password ) {
2019-11-10 09:18:56 -05:00
if ( password === this . cachedPassword ) {
this . cachedPassword = undefined ;
2020-03-20 15:25:23 +00:00
await this . setResetOnAppUninstallTo ( true ) ;
2019-11-10 09:18:56 -05:00
await this . saveToDisk ( ) ;
this . wallets = [ ] ;
this . tx _metadata = [ ] ;
return this . loadFromDisk ( ) ;
} else {
throw new Error ( 'Wrong password. Please, try again.' ) ;
2019-08-23 03:04:23 -04:00
}
}
2019-09-14 00:34:24 -04:00
async isDeleteWalletAfterUninstallEnabled ( ) {
let deleteWalletsAfterUninstall ;
try {
2020-03-20 15:25:23 +00:00
deleteWalletsAfterUninstall = await this . getItem ( AppStorage . DELETE _WALLET _AFTER _UNINSTALL ) ;
2019-09-14 00:34:24 -04:00
} catch ( _e ) {
deleteWalletsAfterUninstall = true ;
}
return ! ! deleteWalletsAfterUninstall ;
}
2018-03-31 01:03:58 +01:00
async encryptStorage ( password ) {
// assuming the storage is not yet encrypted
await this . saveToDisk ( ) ;
2019-08-19 21:18:06 +01:00
let data = await this . getItem ( 'data' ) ;
2018-03-31 01:03:58 +01:00
// TODO: refactor ^^^ (should not save & load to fetch data)
2020-06-01 15:54:23 +03:00
const encrypted = encryption . encrypt ( data , password ) ;
2018-03-31 01:03:58 +01:00
data = [ ] ;
data . push ( encrypted ) ; // putting in array as we might have many buckets with storages
data = JSON . stringify ( data ) ;
2018-04-01 00:16:42 +01:00
this . cachedPassword = password ;
2019-08-21 23:34:03 +01:00
await this . setItem ( 'data' , data ) ;
await this . setItem ( AppStorage . FLAG _ENCRYPTED , '1' ) ;
2019-11-02 20:25:55 -04:00
DeviceQuickActions . clearShortcutItems ( ) ;
DeviceQuickActions . removeAllWallets ( ) ;
2018-03-30 19:31:10 +01:00
}
2018-04-01 00:16:42 +01:00
/ * *
* Cleans up all current application data ( wallets , tx metadata etc )
* Encrypts the bucket and saves it storage
*
* @ returns { Promise . < boolean > } Success or failure
* /
async createFakeStorage ( fakePassword ) {
2020-08-28 12:41:56 +01:00
usedBucketNum = false ; // resetting currently used bucket so we wont overwrite it
2018-04-01 00:16:42 +01:00
this . wallets = [ ] ;
this . tx _metadata = { } ;
2020-06-01 15:54:23 +03:00
const data = {
2018-04-01 00:16:42 +01:00
wallets : [ ] ,
tx _metadata : { } ,
} ;
2019-08-19 21:18:06 +01:00
let buckets = await this . getItem ( 'data' ) ;
2018-04-01 00:16:42 +01:00
buckets = JSON . parse ( buckets ) ;
buckets . push ( encryption . encrypt ( JSON . stringify ( data ) , fakePassword ) ) ;
this . cachedPassword = fakePassword ;
2019-05-02 16:33:03 -04:00
const bucketsString = JSON . stringify ( buckets ) ;
2019-08-21 23:34:03 +01:00
await this . setItem ( 'data' , bucketsString ) ;
2019-08-19 21:18:06 +01:00
return ( await this . getItem ( 'data' ) ) === bucketsString ;
2018-04-01 00:16:42 +01:00
}
2020-08-28 12:41:56 +01:00
hashIt ( s ) {
return createHash ( 'sha256' ) . update ( s ) . digest ( ) . toString ( 'hex' ) ;
}
/ * *
* Returns instace of the Realm database , which is encrypted either by cached user ' s password OR default password .
* Database file is deterministically derived from encryption key .
*
* @ returns { Promise < Realm > }
* /
async getRealm ( ) {
const password = this . hashIt ( this . cachedPassword || 'fyegjitkyf[eqjnc.lf' ) ;
const buf = Buffer . from ( this . hashIt ( password ) + this . hashIt ( password ) , 'hex' ) ;
const encryptionKey = Int8Array . from ( buf ) ;
const path = this . hashIt ( this . hashIt ( password ) ) + '-wallets.realm' ;
const schema = [
{
name : 'Wallet' ,
primaryKey : 'walletid' ,
properties : {
walletid : { type : 'string' , indexed : true } ,
_txs _by _external _index : 'string' , // stringified json
_txs _by _internal _index : 'string' , // stringified json
} ,
} ,
] ;
return Realm . open ( {
schema ,
path ,
encryptionKey ,
} ) ;
}
2018-03-31 01:03:58 +01:00
/ * *
* Loads from storage all wallets and
* maps them to ` this.wallets `
*
* @ param password If present means storage must be decrypted before usage
* @ returns { Promise . < boolean > }
* /
async loadFromDisk ( password ) {
2018-03-20 22:41:07 +02:00
try {
2019-08-19 21:18:06 +01:00
let data = await this . getItem ( 'data' ) ;
2018-03-31 01:03:58 +01:00
if ( password ) {
data = this . decryptData ( data , password ) ;
2018-03-31 14:43:08 +01:00
if ( data ) {
// password is good, cache it
this . cachedPassword = password ;
}
2018-03-31 01:03:58 +01:00
}
2018-03-20 22:41:07 +02:00
if ( data !== null ) {
2020-08-28 12:41:56 +01:00
const realm = await this . getRealm ( ) ;
2018-03-20 22:41:07 +02:00
data = JSON . parse ( data ) ;
if ( ! data . wallets ) return false ;
2020-06-01 15:54:23 +03:00
const wallets = data . wallets ;
for ( const key of wallets ) {
2018-03-20 22:41:07 +02:00
// deciding which type is wallet and instatiating correct object
2020-06-01 15:54:23 +03:00
const tempObj = JSON . parse ( key ) ;
2018-03-20 22:41:07 +02:00
let unserializedWallet ;
switch ( tempObj . type ) {
2019-12-26 20:21:07 -06:00
case PlaceholderWallet . type :
continue ;
2018-12-28 16:52:06 +01:00
case SegwitBech32Wallet . type :
2018-03-20 22:41:07 +02:00
unserializedWallet = SegwitBech32Wallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case SegwitP2SHWallet . type :
2018-03-20 22:41:07 +02:00
unserializedWallet = SegwitP2SHWallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case WatchOnlyWallet . type :
2018-07-14 21:15:55 +01:00
unserializedWallet = WatchOnlyWallet . fromJson ( key ) ;
2019-05-15 00:19:35 +01:00
unserializedWallet . init ( ) ;
2020-07-29 15:52:31 +01:00
if ( unserializedWallet . isHd ( ) && ! unserializedWallet . isXpubValid ( ) ) {
continue ;
}
2018-07-14 21:15:55 +01:00
break ;
2018-12-28 16:52:06 +01:00
case HDLegacyP2PKHWallet . type :
2018-07-22 15:49:59 +01:00
unserializedWallet = HDLegacyP2PKHWallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case HDSegwitP2SHWallet . type :
2018-07-22 15:49:59 +01:00
unserializedWallet = HDSegwitP2SHWallet . fromJson ( key ) ;
break ;
2019-06-01 21:44:39 +01:00
case HDSegwitBech32Wallet . type :
unserializedWallet = HDSegwitBech32Wallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case HDLegacyBreadwalletWallet . type :
2018-07-22 15:49:59 +01:00
unserializedWallet = HDLegacyBreadwalletWallet . fromJson ( key ) ;
break ;
2020-04-22 16:13:18 +01:00
case HDLegacyElectrumSeedP2PKHWallet . type :
unserializedWallet = HDLegacyElectrumSeedP2PKHWallet . fromJson ( key ) ;
break ;
2020-05-04 18:52:01 +09:00
case HDSegwitElectrumSeedP2WPKHWallet . type :
unserializedWallet = HDSegwitElectrumSeedP2WPKHWallet . fromJson ( key ) ;
break ;
2020-10-05 22:25:14 +01:00
case MultisigHDWallet . type :
unserializedWallet = MultisigHDWallet . fromJson ( key ) ;
break ;
2020-06-01 15:54:23 +03:00
case LightningCustodianWallet . type : {
2018-11-04 21:21:07 +00:00
/** @type {LightningCustodianWallet} */
2018-09-01 00:28:19 +01:00
unserializedWallet = LightningCustodianWallet . fromJson ( key ) ;
2018-12-11 22:52:46 +00:00
let lndhub = false ;
2018-11-04 21:21:07 +00:00
try {
2018-12-11 22:52:46 +00:00
lndhub = await AsyncStorage . getItem ( AppStorage . LNDHUB ) ;
2018-11-04 21:21:07 +00:00
} catch ( Error ) {
console . warn ( Error ) ;
}
2019-03-08 21:54:47 +00:00
if ( unserializedWallet . baseURI ) {
unserializedWallet . setBaseURI ( unserializedWallet . baseURI ) ; // not really necessary, just for the sake of readability
console . log ( 'using saved uri for for ln wallet:' , unserializedWallet . baseURI ) ;
} else if ( lndhub ) {
console . log ( 'using wallet-wide settings ' , lndhub , 'for ln wallet' ) ;
2018-12-11 22:52:46 +00:00
unserializedWallet . setBaseURI ( lndhub ) ;
} else {
2019-03-08 21:54:47 +00:00
console . log ( 'using default' , LightningCustodianWallet . defaultBaseUri , 'for ln wallet' ) ;
unserializedWallet . setBaseURI ( LightningCustodianWallet . defaultBaseUri ) ;
2018-11-04 21:21:07 +00:00
}
2018-12-11 22:52:46 +00:00
unserializedWallet . init ( ) ;
2018-09-01 00:28:19 +01:00
break ;
2020-06-01 15:54:23 +03:00
}
2018-12-28 16:52:06 +01:00
case LegacyWallet . type :
2018-03-20 22:41:07 +02:00
default :
unserializedWallet = LegacyWallet . fromJson ( key ) ;
break ;
}
2020-08-28 12:41:56 +01:00
this . inflateWalletFromRealm ( realm , unserializedWallet ) ;
2018-03-20 22:41:07 +02:00
// done
2018-12-29 12:41:38 -05:00
if ( ! this . wallets . some ( wallet => wallet . getSecret ( ) === unserializedWallet . secret ) ) {
this . wallets . push ( unserializedWallet ) ;
this . tx _metadata = data . tx _metadata ;
}
2018-03-20 22:41:07 +02:00
}
2020-08-28 12:41:56 +01:00
realm . close ( ) ;
2020-09-15 21:20:48 -04:00
WatchConnectivity . sendWalletsToWatch ( ) ;
2020-04-07 13:21:37 -04:00
2020-04-07 13:27:01 -04:00
const isStorageEncrypted = await this . storageIsEncrypted ( ) ;
2020-04-07 13:21:37 -04:00
if ( isStorageEncrypted ) {
DeviceQuickActions . clearShortcutItems ( ) ;
DeviceQuickActions . removeAllWallets ( ) ;
} else {
DeviceQuickActions . setWallets ( this . wallets ) ;
DeviceQuickActions . setQuickActions ( ) ;
}
2018-03-31 01:03:58 +01:00
return true ;
} else {
return false ; // failed loading data or loading/decryptin data
2018-03-20 22:41:07 +02:00
}
} catch ( error ) {
2019-05-15 00:19:35 +01:00
console . warn ( error . message ) ;
2018-03-20 22:41:07 +02:00
return false ;
}
}
/ * *
2018-07-02 10:48:40 +01:00
* Lookup wallet in list by it ' s secret and
* remove it from ` this.wallets `
2018-03-20 22:41:07 +02:00
*
* @ param wallet { AbstractWallet }
* /
deleteWallet ( wallet ) {
2020-06-01 15:54:23 +03:00
const secret = wallet . getSecret ( ) ;
const tempWallets = [ ] ;
2019-11-11 01:26:39 -05:00
2020-06-01 15:54:23 +03:00
for ( const value of this . wallets ) {
2018-03-20 22:41:07 +02:00
if ( value . getSecret ( ) === secret ) {
// the one we should delete
// nop
} else {
// the one we must keep
tempWallets . push ( value ) ;
}
}
this . wallets = tempWallets ;
}
2020-08-28 12:41:56 +01:00
inflateWalletFromRealm ( realm , walletToInflate ) {
const wallets = realm . objects ( 'Wallet' ) ;
const filteredWallets = wallets . filtered ( ` walletid = " ${ walletToInflate . getID ( ) } " LIMIT(1) ` ) ;
for ( const realmWalletData of filteredWallets ) {
try {
if ( realmWalletData . _txs _by _external _index ) {
const txsByExternalIndex = JSON . parse ( realmWalletData . _txs _by _external _index ) ;
const txsByInternalIndex = JSON . parse ( realmWalletData . _txs _by _internal _index ) ;
if ( walletToInflate . _hdWalletInstance ) {
walletToInflate . _hdWalletInstance . _txs _by _external _index = txsByExternalIndex ;
walletToInflate . _hdWalletInstance . _txs _by _internal _index = txsByInternalIndex ;
} else {
walletToInflate . _txs _by _external _index = txsByExternalIndex ;
walletToInflate . _txs _by _internal _index = txsByInternalIndex ;
}
}
} catch ( error ) {
console . warn ( error . message ) ;
}
}
}
offloadWalletToRealm ( realm , wallet ) {
const id = wallet . getID ( ) ;
const walletToSave = wallet . _hdWalletInstance ? ? wallet ;
if ( walletToSave instanceof AbstractHDElectrumWallet ) {
realm . write ( ( ) => {
const j1 = JSON . stringify ( walletToSave . _txs _by _external _index ) ;
const j2 = JSON . stringify ( walletToSave . _txs _by _internal _index ) ;
realm . create (
'Wallet' ,
{
walletid : id ,
_txs _by _external _index : j1 ,
_txs _by _internal _index : j2 ,
} ,
Realm . UpdateMode . Modified ,
) ;
} ) ;
}
}
2018-03-31 01:03:58 +01:00
/ * *
* Serializes and saves to storage object data .
* If cached password is saved - finds the correct bucket
* to save to , encrypts and then saves .
*
2019-08-19 21:18:06 +01:00
* @ returns { Promise } Result of storage save
2018-03-31 01:03:58 +01:00
* /
async saveToDisk ( ) {
2020-06-01 15:54:23 +03:00
const walletsToSave = [ ] ;
2020-08-28 12:41:56 +01:00
const realm = await this . getRealm ( ) ;
2020-06-01 15:54:23 +03:00
for ( const key of this . wallets ) {
2019-12-26 20:21:07 -06:00
if ( typeof key === 'boolean' || key . type === PlaceholderWallet . type ) continue ;
2020-10-05 22:25:14 +01:00
key . prepareForSerialization ( ) ;
2020-08-28 12:41:56 +01:00
const keyCloned = Object . assign ( { } , key ) ; // stripped-down version of a wallet to save to secure keystore
if ( key . _hdWalletInstance ) keyCloned . _hdWalletInstance = Object . assign ( { } , key . _hdWalletInstance ) ;
this . offloadWalletToRealm ( realm , key ) ;
// stripping down:
if ( key . _txs _by _external _index ) {
keyCloned . _txs _by _external _index = { } ;
keyCloned . _txs _by _internal _index = { } ;
}
if ( key . _hdWalletInstance ) {
keyCloned . _hdWalletInstance . _txs _by _external _index = { } ;
keyCloned . _hdWalletInstance . _txs _by _internal _index = { } ;
}
walletsToSave . push ( JSON . stringify ( { ... keyCloned , type : keyCloned . type } ) ) ;
2018-03-20 22:41:07 +02:00
}
2020-08-28 12:41:56 +01:00
realm . close ( ) ;
2018-03-20 22:41:07 +02:00
let data = {
wallets : walletsToSave ,
tx _metadata : this . tx _metadata ,
} ;
2018-03-31 01:03:58 +01:00
if ( this . cachedPassword ) {
// should find the correct bucket, encrypt and then save
2019-08-19 21:18:06 +01:00
let buckets = await this . getItem ( 'data' ) ;
2018-03-31 01:03:58 +01:00
buckets = JSON . parse ( buckets ) ;
2020-06-01 15:54:23 +03:00
const newData = [ ] ;
2020-08-28 12:41:56 +01:00
let num = 0 ;
2020-06-01 15:54:23 +03:00
for ( const bucket of buckets ) {
2020-08-28 12:41:56 +01:00
let decrypted ;
// if we had `usedBucketNum` during loadFromDisk(), no point to try to decode each bucket to find the one we
// need, we just to find bucket with the same index
if ( usedBucketNum !== false ) {
if ( num === usedBucketNum ) {
decrypted = true ;
}
num ++ ;
} else {
// we dont have `usedBucketNum` for whatever reason, so lets try to decrypt each bucket after bucket
// till we find the right one
decrypted = encryption . decrypt ( bucket , this . cachedPassword ) ;
}
2018-03-31 01:03:58 +01:00
if ( ! decrypted ) {
// no luck decrypting, its not our bucket
newData . push ( bucket ) ;
} else {
// decrypted ok, this is our bucket
// we serialize our object's data, encrypt it, and add it to buckets
2018-07-07 14:04:32 +01:00
newData . push ( encryption . encrypt ( JSON . stringify ( data ) , this . cachedPassword ) ) ;
2019-08-21 23:34:03 +01:00
await this . setItem ( AppStorage . FLAG _ENCRYPTED , '1' ) ;
2018-03-31 01:03:58 +01:00
}
}
data = newData ;
2018-03-31 14:43:08 +01:00
} else {
2019-08-21 23:34:03 +01:00
await this . setItem ( AppStorage . FLAG _ENCRYPTED , '' ) ; // drop the flag
2018-03-31 01:03:58 +01:00
}
2020-09-07 07:35:08 -04:00
WatchConnectivity . sendWalletsToWatch ( ) ;
2019-11-02 20:25:55 -04:00
DeviceQuickActions . setWallets ( this . wallets ) ;
DeviceQuickActions . setQuickActions ( ) ;
2020-07-02 14:43:35 +01:00
try {
return await this . setItem ( 'data' , JSON . stringify ( data ) ) ;
} catch ( error ) {
alert ( error . message ) ;
}
2018-03-20 22:41:07 +02:00
}
2018-06-17 11:46:19 +01:00
/ * *
* For each wallet , fetches balance from remote endpoint .
* Use getter for a specific wallet to get actual balance .
* Returns void .
2018-06-28 02:43:28 +01:00
* If index is present then fetch only from this specific wallet
2018-06-17 11:46:19 +01:00
*
* @ return { Promise . < void > }
* /
2018-06-28 02:43:28 +01:00
async fetchWalletBalances ( index ) {
2020-05-19 18:16:30 +01:00
console . log ( 'fetchWalletBalances for wallet#' , typeof index === 'undefined' ? '(all)' : index ) ;
2018-06-28 02:43:28 +01:00
if ( index || index === 0 ) {
let c = 0 ;
2020-06-01 15:54:23 +03:00
for ( const wallet of this . wallets . filter ( wallet => wallet . type !== PlaceholderWallet . type ) ) {
2018-06-28 02:43:28 +01:00
if ( c ++ === index ) {
await wallet . fetchBalance ( ) ;
}
}
} else {
2020-06-01 15:54:23 +03:00
for ( const wallet of this . wallets . filter ( wallet => wallet . type !== PlaceholderWallet . type ) ) {
2018-06-28 02:43:28 +01:00
await wallet . fetchBalance ( ) ;
}
2018-03-20 22:41:07 +02:00
}
}
2018-06-17 11:46:19 +01:00
/ * *
* Fetches from remote endpoint all transactions for each wallet .
* Returns void .
* To access transactions - get them from each respective wallet .
2018-06-28 02:43:28 +01:00
* If index is present then fetch only from this specific wallet .
2018-06-17 11:46:19 +01:00
*
2018-06-24 23:22:46 +01:00
* @ param index { Integer } Index of the wallet in this . wallets array ,
* blank to fetch from all wallets
2018-06-17 11:46:19 +01:00
* @ return { Promise . < void > }
* /
2018-06-24 23:22:46 +01:00
async fetchWalletTransactions ( index ) {
2020-05-19 18:16:30 +01:00
console . log ( 'fetchWalletTransactions for wallet#' , typeof index === 'undefined' ? '(all)' : index ) ;
2018-06-24 23:22:46 +01:00
if ( index || index === 0 ) {
let c = 0 ;
2020-06-01 15:54:23 +03:00
for ( const wallet of this . wallets . filter ( wallet => wallet . type !== PlaceholderWallet . type ) ) {
2018-06-24 23:22:46 +01:00
if ( c ++ === index ) {
await wallet . fetchTransactions ( ) ;
2018-12-25 19:07:43 +00:00
if ( wallet . fetchPendingTransactions ) {
await wallet . fetchPendingTransactions ( ) ;
}
if ( wallet . fetchUserInvoices ) {
await wallet . fetchUserInvoices ( ) ;
}
2018-06-24 23:22:46 +01:00
}
}
} else {
2020-06-01 15:54:23 +03:00
for ( const wallet of this . wallets ) {
2018-06-24 23:22:46 +01:00
await wallet . fetchTransactions ( ) ;
2018-12-25 19:07:43 +00:00
if ( wallet . fetchPendingTransactions ) {
await wallet . fetchPendingTransactions ( ) ;
}
if ( wallet . fetchUserInvoices ) {
await wallet . fetchUserInvoices ( ) ;
}
2018-06-24 23:22:46 +01:00
}
2018-03-20 22:41:07 +02:00
}
}
/ * *
*
* @ returns { Array . < AbstractWallet > }
* /
getWallets ( ) {
return this . wallets ;
}
2018-06-17 11:46:19 +01:00
/ * *
2018-06-24 23:22:46 +01:00
* Getter for all transactions in all wallets .
* But if index is provided - only for wallet with corresponding index
2018-06-17 11:46:19 +01:00
*
2019-02-18 23:37:53 +00:00
* @ param index { Integer | null } Wallet index in this . wallets . Empty ( or null ) for all wallets .
* @ param limit { Integer } How many txs return , starting from the earliest . Default : all of them .
2020-06-18 10:59:44 -04:00
* @ param includeWalletsWithHideTransactionsEnabled { Boolean } Wallets ' _hideTransactionsInWalletsList property determines wether the user wants this wallet' s txs hidden from the main list view .
2018-06-17 11:46:19 +01:00
* @ return { Array }
* /
2020-06-18 10:59:44 -04:00
getTransactions ( index , limit = Infinity , includeWalletsWithHideTransactionsEnabled = false ) {
2018-06-24 23:22:46 +01:00
if ( index || index === 0 ) {
let txs = [ ] ;
let c = 0 ;
2020-06-01 15:54:23 +03:00
for ( const wallet of this . wallets ) {
2018-06-24 23:22:46 +01:00
if ( c ++ === index ) {
2018-07-22 15:49:59 +01:00
txs = txs . concat ( wallet . getTransactions ( ) ) ;
2018-06-24 23:22:46 +01:00
}
}
return txs ;
}
2018-03-20 22:41:07 +02:00
let txs = [ ] ;
2020-06-18 10:59:44 -04:00
for ( const wallet of this . wallets . filter ( w => includeWalletsWithHideTransactionsEnabled || ! w . getHideTransactionsInWalletsList ( ) ) ) {
2020-06-01 15:54:23 +03:00
const walletTransactions = wallet . getTransactions ( ) ;
for ( const t of walletTransactions ) {
2019-01-06 23:54:22 -05:00
t . walletPreferredBalanceUnit = wallet . getPreferredBalanceUnit ( ) ;
}
txs = txs . concat ( walletTransactions ) ;
2018-03-20 22:41:07 +02:00
}
2018-10-09 00:25:36 -04:00
2020-06-01 15:54:23 +03:00
for ( const t of txs ) {
2018-10-09 00:25:36 -04:00
t . sort _ts = + new Date ( t . received ) ;
}
2019-02-18 23:37:53 +00:00
return txs
2020-06-01 15:54:23 +03:00
. sort ( function ( a , b ) {
2019-02-18 23:37:53 +00:00
return b . sort _ts - a . sort _ts ;
} )
. slice ( 0 , limit ) ;
2018-03-20 22:41:07 +02:00
}
2018-06-17 11:46:19 +01:00
/ * *
* Getter for a sum of all balances of all wallets
*
* @ return { number }
* /
2018-03-20 22:41:07 +02:00
getBalance ( ) {
let finalBalance = 0 ;
2020-06-01 15:54:23 +03:00
for ( const wal of this . wallets ) {
2019-11-28 23:16:04 +00:00
finalBalance += wal . getBalance ( ) ;
2018-03-20 22:41:07 +02:00
}
return finalBalance ;
}
2019-07-19 01:22:03 +01:00
2020-06-15 19:47:54 +01:00
async getHodlHodlApiKey ( ) {
try {
return await this . getItem ( AppStorage . HODL _HODL _API _KEY ) ;
} catch ( _ ) { }
return false ;
}
async getHodlHodlSignatureKey ( ) {
try {
return await this . getItem ( AppStorage . HODL _HODL _SIGNATURE _KEY ) ;
} catch ( _ ) { }
return false ;
}
/ * *
* Since we cant fetch list of contracts from hodlhodl api yet , we have to keep track of it ourselves
*
* @ returns { Promise < string [ ] > } String ids of contracts in an array
* /
async getHodlHodlContracts ( ) {
try {
2020-06-18 10:59:44 -04:00
const json = await this . getItem ( AppStorage . HODL _HODL _CONTRACTS ) ;
2020-06-15 19:47:54 +01:00
return JSON . parse ( json ) ;
} catch ( _ ) { }
return [ ] ;
}
async addHodlHodlContract ( id ) {
let json ;
try {
json = await this . getItem ( AppStorage . HODL _HODL _CONTRACTS ) ;
json = JSON . parse ( json ) ;
} catch ( _ ) {
json = [ ] ;
}
json . push ( id ) ;
return this . setItem ( AppStorage . HODL _HODL _CONTRACTS , JSON . stringify ( json ) ) ;
}
async setHodlHodlApiKey ( key , sigKey ) {
if ( sigKey ) await this . setItem ( AppStorage . HODL _HODL _SIGNATURE _KEY , sigKey ) ;
return this . setItem ( AppStorage . HODL _HODL _API _KEY , key ) ;
}
2020-03-20 15:25:23 +00:00
async isAdancedModeEnabled ( ) {
try {
return ! ! ( await this . getItem ( AppStorage . ADVANCED _MODE _ENABLED ) ) ;
} catch ( _ ) { }
return false ;
}
async setIsAdancedModeEnabled ( value ) {
await this . setItem ( AppStorage . ADVANCED _MODE _ENABLED , value ? '1' : '' ) ;
}
2019-07-19 01:22:03 +01:00
/ * *
* Simple async sleeper function
*
* @ param ms { number } Milliseconds to sleep
* @ returns { Promise < Promise < * > | Promise < * >> }
* /
async sleep ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
}
2019-11-10 09:18:56 -05:00
}