2020-07-02 15:43:35 +02:00
/* global alert */
2019-05-02 22:33:03 +02:00
import AsyncStorage from '@react-native-community/async-storage' ;
2019-08-19 22:18:06 +02:00
import RNSecureKeyStore , { ACCESSIBLE } from 'react-native-secure-key-store' ;
2018-07-22 16:49:59 +02:00
import {
HDLegacyBreadwalletWallet ,
HDSegwitP2SHWallet ,
HDLegacyP2PKHWallet ,
WatchOnlyWallet ,
LegacyWallet ,
SegwitP2SHWallet ,
SegwitBech32Wallet ,
2019-06-01 22:44:39 +02:00
HDSegwitBech32Wallet ,
2019-12-27 03:21:07 +01:00
PlaceholderWallet ,
LightningCustodianWallet ,
2020-04-22 17:13:18 +02:00
HDLegacyElectrumSeedP2PKHWallet ,
2020-05-04 11:52:01 +02:00
HDSegwitElectrumSeedP2WPKHWallet ,
2020-10-05 23:25:14 +02:00
MultisigHDWallet ,
2018-07-22 16:49:59 +02:00
} from './' ;
2020-09-04 04:26:44 +02:00
import { Platform } from 'react-native' ;
2020-07-01 13:56:52 +02:00
const encryption = require ( '../blue_modules/encryption' ) ;
2020-08-28 13:41:56 +02:00
const Realm = require ( 'realm' ) ;
const createHash = require ( 'create-hash' ) ;
let usedBucketNum = false ;
2018-03-20 21:41:07 +01:00
export class AppStorage {
2018-03-31 02:03:58 +02:00
static FLAG _ENCRYPTED = 'data_encrypted' ;
2018-05-28 21:18:11 +02:00
static LANG = 'lang' ;
2018-12-31 22:27:03 +01:00
static EXCHANGE _RATES = 'currency' ;
2018-12-11 23:52:46 +01:00
static LNDHUB = 'lndhub' ;
2019-07-18 12:22:01 +02:00
static ELECTRUM _HOST = 'electrum_host' ;
static ELECTRUM _TCP _PORT = 'electrum_tcp_port' ;
2020-03-26 17:37:11 +01:00
static ELECTRUM _SSL _PORT = 'electrum_ssl_port' ;
2018-12-31 22:27:03 +01:00
static PREFERRED _CURRENCY = 'preferredCurrency' ;
2019-05-19 21:49:42 +02:00
static ADVANCED _MODE _ENABLED = 'advancedmodeenabled' ;
2020-03-20 16:25:23 +01:00
static DELETE _WALLET _AFTER _UNINSTALL = 'deleteWalletAfterUninstall' ;
2020-06-15 20:47:54 +02: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 16:25:23 +01:00
2018-03-20 21:41:07 +01:00
constructor ( ) {
/** {Array.<AbstractWallet>} */
this . wallets = [ ] ;
this . tx _metadata = { } ;
2018-03-31 02:03:58 +02:00
this . cachedPassword = false ;
2018-03-20 21:41:07 +01:00
}
2019-08-19 22:18:06 +02: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 }
* /
2020-10-24 19:20:59 +02:00
setItem = ( key , value ) => {
2019-08-19 22:18:06 +02:00
if ( typeof navigator !== 'undefined' && navigator . product === 'ReactNative' ) {
return RNSecureKeyStore . set ( key , value , { accessible : ACCESSIBLE . WHEN _UNLOCKED } ) ;
} else {
return AsyncStorage . setItem ( key , value ) ;
}
2020-11-10 19:28:26 +01:00
} ;
2019-08-19 22:18:06 +02:00
/ * *
* Wrapper for storage call . Secure store works only in RN environment . AsyncStorage is
* used for cli / tests
*
* @ param key
* @ returns { Promise < any > | * }
* /
2020-11-10 19:28:26 +01:00
getItem = key => {
2019-08-19 22:18:06 +02:00
if ( typeof navigator !== 'undefined' && navigator . product === 'ReactNative' ) {
return RNSecureKeyStore . get ( key ) ;
} else {
return AsyncStorage . getItem ( key ) ;
}
2020-11-10 19:28:26 +01:00
} ;
2019-08-19 22:18:06 +02:00
2020-10-24 19:20:59 +02:00
setResetOnAppUninstallTo = async value => {
2020-09-04 04:26:44 +02: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 16:25:23 +01:00
}
2020-10-24 19:20:59 +02:00
} ;
2019-09-14 06:34:24 +02:00
2020-10-24 19:20:59 +02:00
storageIsEncrypted = async ( ) => {
2018-03-30 20:31:10 +02:00
let data ;
try {
2019-08-19 22:18:06 +02:00
data = await this . getItem ( AppStorage . FLAG _ENCRYPTED ) ;
2018-03-30 20:31:10 +02:00
} catch ( error ) {
return false ;
}
2018-03-31 02:03:58 +02:00
return ! ! data ;
2020-10-24 19:20:59 +02:00
} ;
2018-03-31 02:03:58 +02:00
2020-10-24 19:20:59 +02:00
isPasswordInUse = async password => {
2019-08-23 09:04:23 +02:00
try {
let data = await this . getItem ( 'data' ) ;
data = this . decryptData ( data , password ) ;
2020-03-20 16:25:23 +01:00
return ! ! data ;
2019-08-23 09:04:23 +02:00
} catch ( _e ) {
return false ;
}
2020-10-24 19:20:59 +02:00
} ;
2019-08-23 09:04:23 +02:00
2018-03-31 02:03:58 +02:00
/ * *
* Iterates through all values of ` data ` trying to
* decrypt each one , and returns first one successfully decrypted
*
2020-03-20 16:25:23 +01:00
* @ param data { string } Serialized array
2018-03-31 02:03:58 +02:00
* @ param password
2020-03-20 16:25:23 +01:00
* @ returns { boolean | string } Either STRING of storage data ( which is stringified JSON ) or FALSE , which means failure
2018-03-31 02:03:58 +02:00
* /
decryptData ( data , password ) {
data = JSON . parse ( data ) ;
let decrypted ;
2020-08-28 13:41:56 +02:00
let num = 0 ;
2020-06-01 14:54:23 +02:00
for ( const value of data ) {
2018-03-31 02:03:58 +02:00
try {
decrypted = encryption . decrypt ( value , password ) ;
} catch ( e ) {
console . log ( e . message ) ;
}
if ( decrypted ) {
2020-08-28 13:41:56 +02:00
usedBucketNum = num ;
2018-03-31 02:03:58 +02:00
return decrypted ;
}
2020-08-28 13:41:56 +02:00
num ++ ;
2018-03-30 20:31:10 +02:00
}
2018-03-31 02:03:58 +02:00
return false ;
}
2020-10-24 19:20:59 +02:00
decryptStorage = async password => {
2019-11-10 15:18:56 +01:00
if ( password === this . cachedPassword ) {
this . cachedPassword = undefined ;
2020-03-20 16:25:23 +01:00
await this . setResetOnAppUninstallTo ( true ) ;
2019-11-10 15:18:56 +01:00
await this . saveToDisk ( ) ;
this . wallets = [ ] ;
this . tx _metadata = [ ] ;
return this . loadFromDisk ( ) ;
} else {
2020-11-22 10:20:31 +01:00
throw new Error ( 'Incorrect password. Please, try again.' ) ;
2019-08-23 09:04:23 +02:00
}
2020-10-24 19:20:59 +02:00
} ;
2019-08-23 09:04:23 +02:00
2020-10-24 19:20:59 +02:00
isDeleteWalletAfterUninstallEnabled = async ( ) => {
2019-09-14 06:34:24 +02:00
let deleteWalletsAfterUninstall ;
try {
2020-03-20 16:25:23 +01:00
deleteWalletsAfterUninstall = await this . getItem ( AppStorage . DELETE _WALLET _AFTER _UNINSTALL ) ;
2019-09-14 06:34:24 +02:00
} catch ( _e ) {
deleteWalletsAfterUninstall = true ;
}
return ! ! deleteWalletsAfterUninstall ;
2020-10-24 19:20:59 +02:00
} ;
2019-09-14 06:34:24 +02:00
2020-10-24 19:20:59 +02:00
encryptStorage = async password => {
2018-03-31 02:03:58 +02:00
// assuming the storage is not yet encrypted
await this . saveToDisk ( ) ;
2019-08-19 22:18:06 +02:00
let data = await this . getItem ( 'data' ) ;
2018-03-31 02:03:58 +02:00
// TODO: refactor ^^^ (should not save & load to fetch data)
2020-06-01 14:54:23 +02:00
const encrypted = encryption . encrypt ( data , password ) ;
2018-03-31 02:03:58 +02:00
data = [ ] ;
data . push ( encrypted ) ; // putting in array as we might have many buckets with storages
data = JSON . stringify ( data ) ;
2018-04-01 01:16:42 +02:00
this . cachedPassword = password ;
2019-08-22 00:34:03 +02:00
await this . setItem ( 'data' , data ) ;
await this . setItem ( AppStorage . FLAG _ENCRYPTED , '1' ) ;
2020-10-24 19:20:59 +02:00
} ;
2018-03-30 20:31:10 +02:00
2018-04-01 01:16:42 +02:00
/ * *
* Cleans up all current application data ( wallets , tx metadata etc )
* Encrypts the bucket and saves it storage
*
* @ returns { Promise . < boolean > } Success or failure
* /
2020-10-24 19:20:59 +02:00
createFakeStorage = async fakePassword => {
2020-08-28 13:41:56 +02:00
usedBucketNum = false ; // resetting currently used bucket so we wont overwrite it
2018-04-01 01:16:42 +02:00
this . wallets = [ ] ;
this . tx _metadata = { } ;
2020-06-01 14:54:23 +02:00
const data = {
2018-04-01 01:16:42 +02:00
wallets : [ ] ,
tx _metadata : { } ,
} ;
2019-08-19 22:18:06 +02:00
let buckets = await this . getItem ( 'data' ) ;
2018-04-01 01:16:42 +02:00
buckets = JSON . parse ( buckets ) ;
buckets . push ( encryption . encrypt ( JSON . stringify ( data ) , fakePassword ) ) ;
this . cachedPassword = fakePassword ;
2019-05-02 22:33:03 +02:00
const bucketsString = JSON . stringify ( buckets ) ;
2019-08-22 00:34:03 +02:00
await this . setItem ( 'data' , bucketsString ) ;
2019-08-19 22:18:06 +02:00
return ( await this . getItem ( 'data' ) ) === bucketsString ;
2020-10-24 19:20:59 +02:00
} ;
2018-04-01 01:16:42 +02:00
2020-10-24 19:20:59 +02:00
hashIt = s => {
2020-08-28 13:41:56 +02:00
return createHash ( 'sha256' ) . update ( s ) . digest ( ) . toString ( 'hex' ) ;
2020-10-24 19:20:59 +02:00
} ;
2020-08-28 13:41:56 +02:00
/ * *
* 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 02:03:58 +02: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 21:41:07 +01:00
try {
2019-08-19 22:18:06 +02:00
let data = await this . getItem ( 'data' ) ;
2018-03-31 02:03:58 +02:00
if ( password ) {
data = this . decryptData ( data , password ) ;
2018-03-31 15:43:08 +02:00
if ( data ) {
// password is good, cache it
this . cachedPassword = password ;
}
2018-03-31 02:03:58 +02:00
}
2018-03-20 21:41:07 +01:00
if ( data !== null ) {
2020-08-28 13:41:56 +02:00
const realm = await this . getRealm ( ) ;
2018-03-20 21:41:07 +01:00
data = JSON . parse ( data ) ;
if ( ! data . wallets ) return false ;
2020-06-01 14:54:23 +02:00
const wallets = data . wallets ;
for ( const key of wallets ) {
2018-03-20 21:41:07 +01:00
// deciding which type is wallet and instatiating correct object
2020-06-01 14:54:23 +02:00
const tempObj = JSON . parse ( key ) ;
2018-03-20 21:41:07 +01:00
let unserializedWallet ;
switch ( tempObj . type ) {
2019-12-27 03:21:07 +01:00
case PlaceholderWallet . type :
continue ;
2018-12-28 16:52:06 +01:00
case SegwitBech32Wallet . type :
2018-03-20 21:41:07 +01:00
unserializedWallet = SegwitBech32Wallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case SegwitP2SHWallet . type :
2018-03-20 21:41:07 +01:00
unserializedWallet = SegwitP2SHWallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case WatchOnlyWallet . type :
2018-07-14 22:15:55 +02:00
unserializedWallet = WatchOnlyWallet . fromJson ( key ) ;
2019-05-15 01:19:35 +02:00
unserializedWallet . init ( ) ;
2020-07-29 16:52:31 +02:00
if ( unserializedWallet . isHd ( ) && ! unserializedWallet . isXpubValid ( ) ) {
continue ;
}
2018-07-14 22:15:55 +02:00
break ;
2018-12-28 16:52:06 +01:00
case HDLegacyP2PKHWallet . type :
2018-07-22 16:49:59 +02:00
unserializedWallet = HDLegacyP2PKHWallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case HDSegwitP2SHWallet . type :
2018-07-22 16:49:59 +02:00
unserializedWallet = HDSegwitP2SHWallet . fromJson ( key ) ;
break ;
2019-06-01 22:44:39 +02:00
case HDSegwitBech32Wallet . type :
unserializedWallet = HDSegwitBech32Wallet . fromJson ( key ) ;
break ;
2018-12-28 16:52:06 +01:00
case HDLegacyBreadwalletWallet . type :
2018-07-22 16:49:59 +02:00
unserializedWallet = HDLegacyBreadwalletWallet . fromJson ( key ) ;
break ;
2020-04-22 17:13:18 +02:00
case HDLegacyElectrumSeedP2PKHWallet . type :
unserializedWallet = HDLegacyElectrumSeedP2PKHWallet . fromJson ( key ) ;
break ;
2020-05-04 11:52:01 +02:00
case HDSegwitElectrumSeedP2WPKHWallet . type :
unserializedWallet = HDSegwitElectrumSeedP2WPKHWallet . fromJson ( key ) ;
break ;
2020-10-05 23:25:14 +02:00
case MultisigHDWallet . type :
unserializedWallet = MultisigHDWallet . fromJson ( key ) ;
break ;
2020-06-01 14:54:23 +02:00
case LightningCustodianWallet . type : {
2018-11-04 22:21:07 +01:00
/** @type {LightningCustodianWallet} */
2018-09-01 01:28:19 +02:00
unserializedWallet = LightningCustodianWallet . fromJson ( key ) ;
2018-12-11 23:52:46 +01:00
let lndhub = false ;
2018-11-04 22:21:07 +01:00
try {
2018-12-11 23:52:46 +01:00
lndhub = await AsyncStorage . getItem ( AppStorage . LNDHUB ) ;
2018-11-04 22:21:07 +01:00
} catch ( Error ) {
console . warn ( Error ) ;
}
2019-03-08 22:54:47 +01: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 23:52:46 +01:00
unserializedWallet . setBaseURI ( lndhub ) ;
} else {
2019-03-08 22:54:47 +01:00
console . log ( 'using default' , LightningCustodianWallet . defaultBaseUri , 'for ln wallet' ) ;
unserializedWallet . setBaseURI ( LightningCustodianWallet . defaultBaseUri ) ;
2018-11-04 22:21:07 +01:00
}
2018-12-11 23:52:46 +01:00
unserializedWallet . init ( ) ;
2018-09-01 01:28:19 +02:00
break ;
2020-06-01 14:54:23 +02:00
}
2018-12-28 16:52:06 +01:00
case LegacyWallet . type :
2018-03-20 21:41:07 +01:00
default :
unserializedWallet = LegacyWallet . fromJson ( key ) ;
break ;
}
2020-08-28 13:41:56 +02:00
this . inflateWalletFromRealm ( realm , unserializedWallet ) ;
2018-03-20 21:41:07 +01:00
// done
2018-12-29 18:41:38 +01:00
if ( ! this . wallets . some ( wallet => wallet . getSecret ( ) === unserializedWallet . secret ) ) {
this . wallets . push ( unserializedWallet ) ;
this . tx _metadata = data . tx _metadata ;
}
2018-03-20 21:41:07 +01:00
}
2020-08-28 13:41:56 +02:00
realm . close ( ) ;
2018-03-31 02:03:58 +02:00
return true ;
} else {
return false ; // failed loading data or loading/decryptin data
2018-03-20 21:41:07 +01:00
}
} catch ( error ) {
2019-05-15 01:19:35 +02:00
console . warn ( error . message ) ;
2018-03-20 21:41:07 +01:00
return false ;
}
}
/ * *
2018-07-02 11:48:40 +02:00
* Lookup wallet in list by it ' s secret and
* remove it from ` this.wallets `
2018-03-20 21:41:07 +01:00
*
* @ param wallet { AbstractWallet }
* /
2020-10-24 19:20:59 +02:00
deleteWallet = wallet => {
2020-06-01 14:54:23 +02:00
const secret = wallet . getSecret ( ) ;
const tempWallets = [ ] ;
2019-11-11 07:26:39 +01:00
2020-06-01 14:54:23 +02:00
for ( const value of this . wallets ) {
2020-10-24 19:20:59 +02:00
if ( value . type === PlaceholderWallet . type ) {
continue ;
} else if ( value . getSecret ( ) === secret ) {
2018-03-20 21:41:07 +01:00
// the one we should delete
// nop
} else {
// the one we must keep
tempWallets . push ( value ) ;
}
}
this . wallets = tempWallets ;
2020-10-24 19:20:59 +02:00
} ;
2018-03-20 21:41:07 +01:00
2020-08-28 13:41:56 +02: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 ;
2020-11-10 19:28:26 +01:00
if ( walletToSave . _txs _by _external _index ) {
2020-08-28 13:41:56 +02:00
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 02:03:58 +02: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 22:18:06 +02:00
* @ returns { Promise } Result of storage save
2018-03-31 02:03:58 +02:00
* /
async saveToDisk ( ) {
2020-06-01 14:54:23 +02:00
const walletsToSave = [ ] ;
2020-08-28 13:41:56 +02:00
const realm = await this . getRealm ( ) ;
2020-06-01 14:54:23 +02:00
for ( const key of this . wallets ) {
2019-12-27 03:21:07 +01:00
if ( typeof key === 'boolean' || key . type === PlaceholderWallet . type ) continue ;
2020-10-05 23:25:14 +02:00
key . prepareForSerialization ( ) ;
2020-10-24 19:20:59 +02:00
delete key . current ;
2020-08-28 13:41:56 +02: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 21:41:07 +01:00
}
2020-08-28 13:41:56 +02:00
realm . close ( ) ;
2018-03-20 21:41:07 +01:00
let data = {
wallets : walletsToSave ,
tx _metadata : this . tx _metadata ,
} ;
2018-03-31 02:03:58 +02:00
if ( this . cachedPassword ) {
// should find the correct bucket, encrypt and then save
2019-08-19 22:18:06 +02:00
let buckets = await this . getItem ( 'data' ) ;
2018-03-31 02:03:58 +02:00
buckets = JSON . parse ( buckets ) ;
2020-06-01 14:54:23 +02:00
const newData = [ ] ;
2020-08-28 13:41:56 +02:00
let num = 0 ;
2020-06-01 14:54:23 +02:00
for ( const bucket of buckets ) {
2020-08-28 13:41:56 +02: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 02:03:58 +02: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 15:04:32 +02:00
newData . push ( encryption . encrypt ( JSON . stringify ( data ) , this . cachedPassword ) ) ;
2019-08-22 00:34:03 +02:00
await this . setItem ( AppStorage . FLAG _ENCRYPTED , '1' ) ;
2018-03-31 02:03:58 +02:00
}
}
data = newData ;
2018-03-31 15:43:08 +02:00
} else {
2019-08-22 00:34:03 +02:00
await this . setItem ( AppStorage . FLAG _ENCRYPTED , '' ) ; // drop the flag
2018-03-31 02:03:58 +02:00
}
2020-07-02 15:43:35 +02:00
try {
return await this . setItem ( 'data' , JSON . stringify ( data ) ) ;
} catch ( error ) {
alert ( error . message ) ;
}
2018-03-20 21:41:07 +01:00
}
2018-06-17 12:46:19 +02:00
/ * *
* For each wallet , fetches balance from remote endpoint .
* Use getter for a specific wallet to get actual balance .
* Returns void .
2018-06-28 03:43:28 +02:00
* If index is present then fetch only from this specific wallet
2018-06-17 12:46:19 +02:00
*
* @ return { Promise . < void > }
* /
2020-10-24 19:20:59 +02:00
fetchWalletBalances = async index => {
2020-05-19 19:16:30 +02:00
console . log ( 'fetchWalletBalances for wallet#' , typeof index === 'undefined' ? '(all)' : index ) ;
2018-06-28 03:43:28 +02:00
if ( index || index === 0 ) {
let c = 0 ;
2020-06-01 14:54:23 +02:00
for ( const wallet of this . wallets . filter ( wallet => wallet . type !== PlaceholderWallet . type ) ) {
2018-06-28 03:43:28 +02:00
if ( c ++ === index ) {
await wallet . fetchBalance ( ) ;
}
}
} else {
2020-06-01 14:54:23 +02:00
for ( const wallet of this . wallets . filter ( wallet => wallet . type !== PlaceholderWallet . type ) ) {
2018-06-28 03:43:28 +02:00
await wallet . fetchBalance ( ) ;
}
2018-03-20 21:41:07 +01:00
}
2020-10-24 19:20:59 +02:00
} ;
2018-03-20 21:41:07 +01:00
2018-06-17 12:46:19 +02:00
/ * *
* Fetches from remote endpoint all transactions for each wallet .
* Returns void .
* To access transactions - get them from each respective wallet .
2018-06-28 03:43:28 +02:00
* If index is present then fetch only from this specific wallet .
2018-06-17 12:46:19 +02:00
*
2018-06-25 00:22:46 +02:00
* @ param index { Integer } Index of the wallet in this . wallets array ,
* blank to fetch from all wallets
2018-06-17 12:46:19 +02:00
* @ return { Promise . < void > }
* /
2020-10-24 19:20:59 +02:00
fetchWalletTransactions = async index => {
2020-05-19 19:16:30 +02:00
console . log ( 'fetchWalletTransactions for wallet#' , typeof index === 'undefined' ? '(all)' : index ) ;
2018-06-25 00:22:46 +02:00
if ( index || index === 0 ) {
let c = 0 ;
2020-06-01 14:54:23 +02:00
for ( const wallet of this . wallets . filter ( wallet => wallet . type !== PlaceholderWallet . type ) ) {
2018-06-25 00:22:46 +02:00
if ( c ++ === index ) {
await wallet . fetchTransactions ( ) ;
2018-12-25 20:07:43 +01:00
if ( wallet . fetchPendingTransactions ) {
await wallet . fetchPendingTransactions ( ) ;
}
if ( wallet . fetchUserInvoices ) {
await wallet . fetchUserInvoices ( ) ;
}
2018-06-25 00:22:46 +02:00
}
}
} else {
2020-06-01 14:54:23 +02:00
for ( const wallet of this . wallets ) {
2018-06-25 00:22:46 +02:00
await wallet . fetchTransactions ( ) ;
2018-12-25 20:07:43 +01:00
if ( wallet . fetchPendingTransactions ) {
await wallet . fetchPendingTransactions ( ) ;
}
if ( wallet . fetchUserInvoices ) {
await wallet . fetchUserInvoices ( ) ;
}
2018-06-25 00:22:46 +02:00
}
2018-03-20 21:41:07 +01:00
}
2020-10-24 19:20:59 +02:00
} ;
2018-03-20 21:41:07 +01:00
/ * *
*
* @ returns { Array . < AbstractWallet > }
* /
2020-10-24 19:20:59 +02:00
getWallets = ( ) => {
2018-03-20 21:41:07 +01:00
return this . wallets ;
2020-10-24 19:20:59 +02:00
} ;
2018-03-20 21:41:07 +01:00
2018-06-17 12:46:19 +02:00
/ * *
2018-06-25 00:22:46 +02:00
* Getter for all transactions in all wallets .
* But if index is provided - only for wallet with corresponding index
2018-06-17 12:46:19 +02:00
*
2019-02-19 00:37:53 +01: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 16:59:44 +02: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 12:46:19 +02:00
* @ return { Array }
* /
2020-10-24 19:20:59 +02:00
getTransactions = ( index , limit = Infinity , includeWalletsWithHideTransactionsEnabled = false ) => {
2018-06-25 00:22:46 +02:00
if ( index || index === 0 ) {
let txs = [ ] ;
let c = 0 ;
2020-06-01 14:54:23 +02:00
for ( const wallet of this . wallets ) {
2018-06-25 00:22:46 +02:00
if ( c ++ === index ) {
2018-07-22 16:49:59 +02:00
txs = txs . concat ( wallet . getTransactions ( ) ) ;
2018-06-25 00:22:46 +02:00
}
}
return txs ;
}
2018-03-20 21:41:07 +01:00
let txs = [ ] ;
2020-06-18 16:59:44 +02:00
for ( const wallet of this . wallets . filter ( w => includeWalletsWithHideTransactionsEnabled || ! w . getHideTransactionsInWalletsList ( ) ) ) {
2020-06-01 14:54:23 +02:00
const walletTransactions = wallet . getTransactions ( ) ;
for ( const t of walletTransactions ) {
2019-01-07 05:54:22 +01:00
t . walletPreferredBalanceUnit = wallet . getPreferredBalanceUnit ( ) ;
}
txs = txs . concat ( walletTransactions ) ;
2018-03-20 21:41:07 +01:00
}
2018-10-09 06:25:36 +02:00
2020-06-01 14:54:23 +02:00
for ( const t of txs ) {
2018-10-09 06:25:36 +02:00
t . sort _ts = + new Date ( t . received ) ;
}
2019-02-19 00:37:53 +01:00
return txs
2020-06-01 14:54:23 +02:00
. sort ( function ( a , b ) {
2019-02-19 00:37:53 +01:00
return b . sort _ts - a . sort _ts ;
} )
. slice ( 0 , limit ) ;
2020-10-24 19:20:59 +02:00
} ;
2018-03-20 21:41:07 +01:00
2018-06-17 12:46:19 +02:00
/ * *
* Getter for a sum of all balances of all wallets
*
* @ return { number }
* /
2020-10-24 19:20:59 +02:00
getBalance = ( ) => {
2018-03-20 21:41:07 +01:00
let finalBalance = 0 ;
2020-06-01 14:54:23 +02:00
for ( const wal of this . wallets ) {
2019-11-29 00:16:04 +01:00
finalBalance += wal . getBalance ( ) ;
2018-03-20 21:41:07 +01:00
}
return finalBalance ;
2020-10-24 19:20:59 +02:00
} ;
2019-07-19 02:22:03 +02:00
2020-10-24 19:20:59 +02:00
getHodlHodlApiKey = async ( ) => {
2020-06-15 20:47:54 +02:00
try {
return await this . getItem ( AppStorage . HODL _HODL _API _KEY ) ;
} catch ( _ ) { }
return false ;
2020-10-24 19:20:59 +02:00
} ;
2020-06-15 20:47:54 +02:00
2020-10-24 19:20:59 +02:00
getHodlHodlSignatureKey = async ( ) => {
2020-06-15 20:47:54 +02:00
try {
return await this . getItem ( AppStorage . HODL _HODL _SIGNATURE _KEY ) ;
} catch ( _ ) { }
return false ;
2020-10-24 19:20:59 +02:00
} ;
2020-06-15 20:47:54 +02:00
/ * *
* 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
* /
2020-10-24 19:20:59 +02:00
getHodlHodlContracts = async ( ) => {
2020-06-15 20:47:54 +02:00
try {
2020-06-18 16:59:44 +02:00
const json = await this . getItem ( AppStorage . HODL _HODL _CONTRACTS ) ;
2020-06-15 20:47:54 +02:00
return JSON . parse ( json ) ;
} catch ( _ ) { }
return [ ] ;
2020-10-24 19:20:59 +02:00
} ;
2020-06-15 20:47:54 +02:00
2020-10-24 19:20:59 +02:00
addHodlHodlContract = async id => {
2020-06-15 20:47:54 +02:00
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 ) ) ;
2020-10-24 19:20:59 +02:00
} ;
2020-06-15 20:47:54 +02:00
2020-10-24 19:20:59 +02:00
setHodlHodlApiKey = async ( key , sigKey ) => {
2020-06-15 20:47:54 +02:00
if ( sigKey ) await this . setItem ( AppStorage . HODL _HODL _SIGNATURE _KEY , sigKey ) ;
return this . setItem ( AppStorage . HODL _HODL _API _KEY , key ) ;
2020-10-24 19:20:59 +02:00
} ;
2020-06-15 20:47:54 +02:00
2020-10-24 19:20:59 +02:00
isAdancedModeEnabled = async ( ) => {
2020-03-20 16:25:23 +01:00
try {
return ! ! ( await this . getItem ( AppStorage . ADVANCED _MODE _ENABLED ) ) ;
} catch ( _ ) { }
return false ;
2020-10-24 19:20:59 +02:00
} ;
2020-03-20 16:25:23 +01:00
2020-10-24 19:20:59 +02:00
setIsAdancedModeEnabled = async value => {
2020-03-20 16:25:23 +01:00
await this . setItem ( AppStorage . ADVANCED _MODE _ENABLED , value ? '1' : '' ) ;
2020-10-24 19:20:59 +02:00
} ;
2020-03-20 16:25:23 +01:00
2019-07-19 02:22:03 +02:00
/ * *
* Simple async sleeper function
*
* @ param ms { number } Milliseconds to sleep
* @ returns { Promise < Promise < * > | Promise < * >> }
* /
2020-10-24 19:20:59 +02:00
sleep = ms => {
2019-07-19 02:22:03 +02:00
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
2020-10-24 19:20:59 +02:00
} ;
2019-11-10 15:18:56 +01:00
}